From: Jeff Dike This patch fixes some copy_user bugs: - kernelspace page faults that happen on behalf of a process are now correctly handled - add copy_user treatment so a fault handler which looks at the faulting instruction - added a note to do the same with the ldt stuff some day Signed-off-by: Jeff Dike Signed-off-by: Andrew Morton --- 25-akpm/arch/um/include/user_util.h | 1 25-akpm/arch/um/kernel/skas/uaccess.c | 57 ++++++++++++++++++++++++++-------- 25-akpm/arch/um/kernel/user_util.c | 16 +++++++++ 25-akpm/arch/um/sys-i386/bugs.c | 7 ++-- 25-akpm/arch/um/sys-i386/ldt.c | 2 + 5 files changed, 67 insertions(+), 16 deletions(-) diff -puN arch/um/include/user_util.h~uml-copy_user-fixes arch/um/include/user_util.h --- 25/arch/um/include/user_util.h~uml-copy_user-fixes Tue Sep 14 18:29:43 2004 +++ 25-akpm/arch/um/include/user_util.h Tue Sep 14 18:29:43 2004 @@ -88,6 +88,7 @@ extern int arch_fixup(unsigned long addr extern void forward_pending_sigio(int target); extern int can_do_skas(void); extern void arch_init_thread(void); +extern int setjmp_wrapper(void (*proc)(void *, void *), ...); extern int __raw(int fd, int complain, int now); #define raw(fd, complain) __raw((fd), (complain), 1) diff -puN arch/um/kernel/skas/uaccess.c~uml-copy_user-fixes arch/um/kernel/skas/uaccess.c --- 25/arch/um/kernel/skas/uaccess.c~uml-copy_user-fixes Tue Sep 14 18:29:43 2004 +++ 25-akpm/arch/um/kernel/skas/uaccess.c Tue Sep 14 18:29:43 2004 @@ -12,6 +12,7 @@ #include "asm/pgtable.h" #include "asm/uaccess.h" #include "kern_util.h" +#include "user_util.h" extern void *um_virt_to_phys(struct task_struct *task, unsigned long addr, pte_t *pte_out); @@ -51,37 +52,67 @@ static int do_op(unsigned long addr, int return(n); } -static int buffer_op(unsigned long addr, int len, int is_write, - int (*op)(unsigned long addr, int len, void *arg), - void *arg) +static void do_buffer_op(void *jmpbuf, void *arg_ptr) { + va_list args = *((va_list *) arg_ptr); + unsigned long addr = va_arg(args, unsigned long); + int len = va_arg(args, int); + int is_write = va_arg(args, int); + int (*op)(unsigned long, int, void *) = va_arg(args, void *); + void *arg = va_arg(args, void *); + int *res = va_arg(args, int *); int size = min(PAGE_ALIGN(addr) - addr, (unsigned long) len); int remain = len, n; + current->thread.fault_catcher = jmpbuf; n = do_op(addr, size, is_write, op, arg); - if(n != 0) - return(n < 0 ? remain : 0); + if(n != 0){ + *res = (n < 0 ? remain : 0); + goto out; + } addr += size; remain -= size; - if(remain == 0) - return(0); + if(remain == 0){ + *res = 0; + goto out; + } while(addr < ((addr + remain) & PAGE_MASK)){ n = do_op(addr, PAGE_SIZE, is_write, op, arg); - if(n != 0) - return(n < 0 ? remain : 0); + if(n != 0){ + *res = (n < 0 ? remain : 0); + goto out; + } addr += PAGE_SIZE; remain -= PAGE_SIZE; } - if(remain == 0) - return(0); + if(remain == 0){ + *res = 0; + goto out; + } n = do_op(addr, remain, is_write, op, arg); if(n != 0) - return(n < 0 ? remain : 0); - return(0); + *res = (n < 0 ? remain : 0); + else *res = 0; + out: + current->thread.fault_catcher = NULL; +} + +static int buffer_op(unsigned long addr, int len, int is_write, + int (*op)(unsigned long addr, int len, void *arg), + void *arg) +{ + int faulted, res; + + faulted = setjmp_wrapper(do_buffer_op, addr, len, is_write, op, arg, + &res); + if(!faulted) + return(res); + + return(addr + len - (unsigned long) current->thread.fault_addr); } static int copy_chunk_from_user(unsigned long from, int len, void *arg) diff -puN arch/um/kernel/user_util.c~uml-copy_user-fixes arch/um/kernel/user_util.c --- 25/arch/um/kernel/user_util.c~uml-copy_user-fixes Tue Sep 14 18:29:43 2004 +++ 25-akpm/arch/um/kernel/user_util.c Tue Sep 14 18:29:43 2004 @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -169,6 +170,21 @@ void setup_hostinfo(void) host.release, host.version, host.machine); } +int setjmp_wrapper(void (*proc)(void *, void *), ...) +{ + va_list args; + sigjmp_buf buf; + int n; + + n = sigsetjmp(buf, 1); + if(n == 0){ + va_start(args, proc); + (*proc)(&buf, &args); + } + va_end(args); + return(n); +} + /* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically diff -puN arch/um/sys-i386/bugs.c~uml-copy_user-fixes arch/um/sys-i386/bugs.c --- 25/arch/um/sys-i386/bugs.c~uml-copy_user-fixes Tue Sep 14 18:29:43 2004 +++ 25-akpm/arch/um/sys-i386/bugs.c Tue Sep 14 18:29:43 2004 @@ -183,15 +183,16 @@ void arch_check_bugs(void) int arch_handle_signal(int sig, union uml_pt_regs *regs) { - unsigned long ip; + unsigned char tmp[2]; /* This is testing for a cmov (0x0f 0x4x) instruction causing a * SIGILL in init. */ if((sig != SIGILL) || (TASK_PID(get_current()) != 1)) return(0); - ip = UPT_IP(regs); - if((*((char *) ip) != 0x0f) || ((*((char *) (ip + 1)) & 0xf0) != 0x40)) + if (copy_from_user_proc(tmp, (void *) UPT_IP(regs), 2)) + panic("SIGILL in init, could not read instructions!\n"); + if((tmp[0] != 0x0f) || ((tmp[1] & 0xf0) != 0x40)) return(0); if(host_has_cmov == 0) diff -puN arch/um/sys-i386/ldt.c~uml-copy_user-fixes arch/um/sys-i386/ldt.c --- 25/arch/um/sys-i386/ldt.c~uml-copy_user-fixes Tue Sep 14 18:29:43 2004 +++ 25-akpm/arch/um/sys-i386/ldt.c Tue Sep 14 18:29:43 2004 @@ -13,6 +13,8 @@ #ifdef CONFIG_MODE_TT extern int modify_ldt(int func, void *ptr, unsigned long bytecount); +/* XXX this needs copy_to_user and copy_from_user */ + int sys_modify_ldt_tt(int func, void *ptr, unsigned long bytecount) { if(verify_area(VERIFY_READ, ptr, bytecount)) return(-EFAULT); _