uaccess.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. /*
  2. * Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com)
  3. * Licensed under the GPL
  4. */
  5. #include "linux/compiler.h"
  6. #include "linux/stddef.h"
  7. #include "linux/kernel.h"
  8. #include "linux/string.h"
  9. #include "linux/fs.h"
  10. #include "linux/hardirq.h"
  11. #include "linux/highmem.h"
  12. #include "asm/page.h"
  13. #include "asm/pgtable.h"
  14. #include "asm/uaccess.h"
  15. #include "kern_util.h"
  16. #include "os.h"
  17. extern void *um_virt_to_phys(struct task_struct *task, unsigned long addr,
  18. pte_t *pte_out);
  19. static unsigned long maybe_map(unsigned long virt, int is_write)
  20. {
  21. pte_t pte;
  22. int err;
  23. void *phys = um_virt_to_phys(current, virt, &pte);
  24. int dummy_code;
  25. if(IS_ERR(phys) || (is_write && !pte_write(pte))){
  26. err = handle_page_fault(virt, 0, is_write, 1, &dummy_code);
  27. if(err)
  28. return(-1UL);
  29. phys = um_virt_to_phys(current, virt, NULL);
  30. }
  31. if(IS_ERR(phys))
  32. phys = (void *) -1;
  33. return((unsigned long) phys);
  34. }
  35. static int do_op_one_page(unsigned long addr, int len, int is_write,
  36. int (*op)(unsigned long addr, int len, void *arg), void *arg)
  37. {
  38. struct page *page;
  39. int n;
  40. addr = maybe_map(addr, is_write);
  41. if(addr == -1UL)
  42. return(-1);
  43. page = phys_to_page(addr);
  44. addr = (unsigned long) kmap_atomic(page, KM_UML_USERCOPY) + (addr & ~PAGE_MASK);
  45. n = (*op)(addr, len, arg);
  46. kunmap_atomic(page, KM_UML_USERCOPY);
  47. return(n);
  48. }
  49. static void do_buffer_op(void *jmpbuf, void *arg_ptr)
  50. {
  51. va_list args;
  52. unsigned long addr;
  53. int len, is_write, size, remain, n;
  54. int (*op)(unsigned long, int, void *);
  55. void *arg;
  56. int *res;
  57. va_copy(args, *(va_list *)arg_ptr);
  58. addr = va_arg(args, unsigned long);
  59. len = va_arg(args, int);
  60. is_write = va_arg(args, int);
  61. op = va_arg(args, void *);
  62. arg = va_arg(args, void *);
  63. res = va_arg(args, int *);
  64. va_end(args);
  65. size = min(PAGE_ALIGN(addr) - addr, (unsigned long) len);
  66. remain = len;
  67. current->thread.fault_catcher = jmpbuf;
  68. n = do_op_one_page(addr, size, is_write, op, arg);
  69. if(n != 0){
  70. *res = (n < 0 ? remain : 0);
  71. goto out;
  72. }
  73. addr += size;
  74. remain -= size;
  75. if(remain == 0){
  76. *res = 0;
  77. goto out;
  78. }
  79. while(addr < ((addr + remain) & PAGE_MASK)){
  80. n = do_op_one_page(addr, PAGE_SIZE, is_write, op, arg);
  81. if(n != 0){
  82. *res = (n < 0 ? remain : 0);
  83. goto out;
  84. }
  85. addr += PAGE_SIZE;
  86. remain -= PAGE_SIZE;
  87. }
  88. if(remain == 0){
  89. *res = 0;
  90. goto out;
  91. }
  92. n = do_op_one_page(addr, remain, is_write, op, arg);
  93. if(n != 0)
  94. *res = (n < 0 ? remain : 0);
  95. else *res = 0;
  96. out:
  97. current->thread.fault_catcher = NULL;
  98. }
  99. static int buffer_op(unsigned long addr, int len, int is_write,
  100. int (*op)(unsigned long addr, int len, void *arg),
  101. void *arg)
  102. {
  103. int faulted, res;
  104. faulted = setjmp_wrapper(do_buffer_op, addr, len, is_write, op, arg,
  105. &res);
  106. if(!faulted)
  107. return(res);
  108. return(addr + len - (unsigned long) current->thread.fault_addr);
  109. }
  110. static int copy_chunk_from_user(unsigned long from, int len, void *arg)
  111. {
  112. unsigned long *to_ptr = arg, to = *to_ptr;
  113. memcpy((void *) to, (void *) from, len);
  114. *to_ptr += len;
  115. return(0);
  116. }
  117. int copy_from_user_skas(void *to, const void __user *from, int n)
  118. {
  119. if(segment_eq(get_fs(), KERNEL_DS)){
  120. memcpy(to, (__force void*)from, n);
  121. return(0);
  122. }
  123. return(access_ok(VERIFY_READ, from, n) ?
  124. buffer_op((unsigned long) from, n, 0, copy_chunk_from_user, &to):
  125. n);
  126. }
  127. static int copy_chunk_to_user(unsigned long to, int len, void *arg)
  128. {
  129. unsigned long *from_ptr = arg, from = *from_ptr;
  130. memcpy((void *) to, (void *) from, len);
  131. *from_ptr += len;
  132. return(0);
  133. }
  134. int copy_to_user_skas(void __user *to, const void *from, int n)
  135. {
  136. if(segment_eq(get_fs(), KERNEL_DS)){
  137. memcpy((__force void*)to, from, n);
  138. return(0);
  139. }
  140. return(access_ok(VERIFY_WRITE, to, n) ?
  141. buffer_op((unsigned long) to, n, 1, copy_chunk_to_user, &from) :
  142. n);
  143. }
  144. static int strncpy_chunk_from_user(unsigned long from, int len, void *arg)
  145. {
  146. char **to_ptr = arg, *to = *to_ptr;
  147. int n;
  148. strncpy(to, (void *) from, len);
  149. n = strnlen(to, len);
  150. *to_ptr += n;
  151. if(n < len)
  152. return(1);
  153. return(0);
  154. }
  155. int strncpy_from_user_skas(char *dst, const char __user *src, int count)
  156. {
  157. int n;
  158. char *ptr = dst;
  159. if(segment_eq(get_fs(), KERNEL_DS)){
  160. strncpy(dst, (__force void*)src, count);
  161. return(strnlen(dst, count));
  162. }
  163. if(!access_ok(VERIFY_READ, src, 1))
  164. return(-EFAULT);
  165. n = buffer_op((unsigned long) src, count, 0, strncpy_chunk_from_user,
  166. &ptr);
  167. if(n != 0)
  168. return(-EFAULT);
  169. return(strnlen(dst, count));
  170. }
  171. static int clear_chunk(unsigned long addr, int len, void *unused)
  172. {
  173. memset((void *) addr, 0, len);
  174. return(0);
  175. }
  176. int __clear_user_skas(void __user *mem, int len)
  177. {
  178. return(buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL));
  179. }
  180. int clear_user_skas(void __user *mem, int len)
  181. {
  182. if(segment_eq(get_fs(), KERNEL_DS)){
  183. memset((__force void*)mem, 0, len);
  184. return(0);
  185. }
  186. return(access_ok(VERIFY_WRITE, mem, len) ?
  187. buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL) : len);
  188. }
  189. static int strnlen_chunk(unsigned long str, int len, void *arg)
  190. {
  191. int *len_ptr = arg, n;
  192. n = strnlen((void *) str, len);
  193. *len_ptr += n;
  194. if(n < len)
  195. return(1);
  196. return(0);
  197. }
  198. int strnlen_user_skas(const void __user *str, int len)
  199. {
  200. int count = 0, n;
  201. if(segment_eq(get_fs(), KERNEL_DS))
  202. return(strnlen((__force char*)str, len) + 1);
  203. n = buffer_op((unsigned long) str, len, 0, strnlen_chunk, &count);
  204. if(n == 0)
  205. return(count + 1);
  206. return(-EFAULT);
  207. }
  208. /*
  209. * Overrides for Emacs so that we follow Linus's tabbing style.
  210. * Emacs will notice this stuff at the end of the file and automatically
  211. * adjust the settings for this buffer only. This must remain at the end
  212. * of the file.
  213. * ---------------------------------------------------------------------------
  214. * Local variables:
  215. * c-file-style: "linux"
  216. * End:
  217. */