uaccess.c 5.6 KB

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