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