uaccess.c 5.2 KB

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