diff options
| -rw-r--r-- | arch/um/include/user_util.h | 1 | ||||
| -rw-r--r-- | arch/um/kernel/skas/uaccess.c | 57 | ||||
| -rw-r--r-- | arch/um/kernel/user_util.c | 16 | ||||
| -rw-r--r-- | arch/um/sys-i386/bugs.c | 7 | ||||
| -rw-r--r-- | arch/um/sys-i386/ldt.c | 2 |
5 files changed, 67 insertions, 16 deletions
diff --git a/arch/um/include/user_util.h b/arch/um/include/user_util.h index 89ccc90e7d89..0ba8eb056deb 100644 --- a/arch/um/include/user_util.h +++ b/arch/um/include/user_util.h @@ -88,6 +88,7 @@ extern int arch_fixup(unsigned long address, void *sc_ptr); 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 --git a/arch/um/kernel/skas/uaccess.c b/arch/um/kernel/skas/uaccess.c index 7fa723278620..77048cd6775c 100644 --- a/arch/um/kernel/skas/uaccess.c +++ b/arch/um/kernel/skas/uaccess.c @@ -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 len, int is_write, 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 --git a/arch/um/kernel/user_util.c b/arch/um/kernel/user_util.c index a3b17b05d536..a366c189c638 100644 --- a/arch/um/kernel/user_util.c +++ b/arch/um/kernel/user_util.c @@ -8,6 +8,7 @@ #include <unistd.h> #include <limits.h> #include <sys/mman.h> +#include <setjmp.h> #include <sys/stat.h> #include <sys/ptrace.h> #include <sys/utsname.h> @@ -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 --git a/arch/um/sys-i386/bugs.c b/arch/um/sys-i386/bugs.c index 9bff9aa6093c..41b0ab2fe830 100644 --- a/arch/um/sys-i386/bugs.c +++ b/arch/um/sys-i386/bugs.c @@ -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 --git a/arch/um/sys-i386/ldt.c b/arch/um/sys-i386/ldt.c index 33e302160d7c..ba77ccaa8361 100644 --- a/arch/um/sys-i386/ldt.c +++ b/arch/um/sys-i386/ldt.c @@ -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); |
