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