From eb0700359bd2a24e9347edc0ff54ee8e69f02d1c Mon Sep 17 00:00:00 2001 From: Tomoki Shirasawa Date: Thu, 10 Mar 2016 10:33:38 +0900 Subject: [PATCH] fix REQ-36 --- arch/x86/kernel/memory.c | 174 +++++++++++++++++++++++++++++++++++++++ kernel/syscall.c | 4 +- lib/include/string.h | 1 + lib/string.c | 71 ++++++++++++++++ 4 files changed, 248 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/memory.c b/arch/x86/kernel/memory.c index 3cdf8d73..79421f46 100644 --- a/arch/x86/kernel/memory.c +++ b/arch/x86/kernel/memory.c @@ -2184,6 +2184,128 @@ int copy_from_user(void *dst, const void *src, size_t siz) return 0; } +int strlen_user(const char *s) +{ + struct process_vm *vm = cpu_local_var(current)->vm; + struct vm_range *range; + unsigned long pgstart; + int maxlen; + const char *head = s; + + maxlen = 4096 - (((unsigned long)s) & 0x0000000000000fffUL); + pgstart = ((unsigned long)s) & 0xfffffffffffff000UL; + if(!pgstart || pgstart >= MAP_KERNEL_START) + return -EFAULT; + ihk_mc_spinlock_lock_noirq(&vm->memory_range_lock); + for(;;){ + range = lookup_process_memory_range(vm, pgstart, pgstart+1); + if(range == NULL){ + ihk_mc_spinlock_unlock_noirq(&vm->memory_range_lock); + return -EFAULT; + } + if((range->flag & VR_PROT_MASK) == VR_PROT_NONE){ + ihk_mc_spinlock_unlock_noirq(&vm->memory_range_lock); + return -EFAULT; + } + while(*s && maxlen > 0){ + s++; + maxlen--; + } + if(!*s) + break; + maxlen = 4096; + pgstart += 4096; + } + ihk_mc_spinlock_unlock_noirq(&vm->memory_range_lock); + return s - head; +} + +int strcpy_from_user(char *dst, const char *src) +{ + struct process_vm *vm = cpu_local_var(current)->vm; + struct vm_range *range; + unsigned long pgstart; + int maxlen; + int err = 0; + + maxlen = 4096 - (((unsigned long)src) & 0x0000000000000fffUL); + pgstart = ((unsigned long)src) & 0xfffffffffffff000UL; + if(!pgstart || pgstart >= MAP_KERNEL_START) + return -EFAULT; + ihk_mc_spinlock_lock_noirq(&vm->memory_range_lock); + for(;;){ + range = lookup_process_memory_range(vm, pgstart, pgstart + 1); + if(range == NULL){ + err = -EFAULT; + break; + } + if((range->flag & VR_PROT_MASK) == VR_PROT_NONE){ + err = -EFAULT; + break; + } + while(*src && maxlen > 0){ + *(dst++) = *(src++); + maxlen--; + } + if(!*src){ + *dst = '\0'; + break; + } + maxlen = 4096; + pgstart += 4096; + } + ihk_mc_spinlock_unlock_noirq(&vm->memory_range_lock); + return err; +} + +long getlong_user(const long *p) +{ + struct process_vm *vm = cpu_local_var(current)->vm; + struct vm_range *range; + unsigned long plong = (unsigned long)p; + unsigned long pgstart; + + pgstart = plong & 0xfffffffffffff000UL; + if(!pgstart || pgstart >= MAP_KERNEL_START) + return -EFAULT; + ihk_mc_spinlock_lock_noirq(&vm->memory_range_lock); + range = lookup_process_memory_range(vm, plong, plong + sizeof(long)); + if(range == NULL){ + ihk_mc_spinlock_unlock_noirq(&vm->memory_range_lock); + return -EFAULT; + } + if((range->flag & VR_PROT_MASK) == VR_PROT_NONE){ + ihk_mc_spinlock_unlock_noirq(&vm->memory_range_lock); + return -EFAULT; + } + ihk_mc_spinlock_unlock_noirq(&vm->memory_range_lock); + return *p; +} + +int getint_user(const int *p) +{ + struct process_vm *vm = cpu_local_var(current)->vm; + struct vm_range *range; + unsigned long pint = (unsigned long)p; + unsigned long pgstart; + + pgstart = pint & 0xfffffffffffff000UL; + if(!pgstart || pgstart >= MAP_KERNEL_START) + return -EFAULT; + ihk_mc_spinlock_lock_noirq(&vm->memory_range_lock); + range = lookup_process_memory_range(vm, pint, pint + sizeof(int)); + if(range == NULL){ + ihk_mc_spinlock_unlock_noirq(&vm->memory_range_lock); + return -EFAULT; + } + if((range->flag & VR_PROT_MASK) == VR_PROT_NONE){ + ihk_mc_spinlock_unlock_noirq(&vm->memory_range_lock); + return -EFAULT; + } + ihk_mc_spinlock_unlock_noirq(&vm->memory_range_lock); + return *p; +} + int read_process_vm(struct process_vm *vm, void *kdst, const void *usrc, size_t siz) { const uintptr_t ustart = (uintptr_t)usrc; @@ -2267,6 +2389,58 @@ int copy_to_user(void *dst, const void *src, size_t siz) return 0; } +int setlong_user(long *dst, long data) +{ + struct process_vm *vm = cpu_local_var(current)->vm; + struct vm_range *range; + unsigned long plong = (unsigned long)dst; + unsigned long pgstart; + + pgstart = plong & 0xfffffffffffff000UL; + if(!pgstart || pgstart >= MAP_KERNEL_START) + return -EFAULT; + ihk_mc_spinlock_lock_noirq(&vm->memory_range_lock); + range = lookup_process_memory_range(vm, plong, plong + sizeof(long)); + if(range == NULL){ + ihk_mc_spinlock_unlock_noirq(&vm->memory_range_lock); + return -EFAULT; + } + if(((range->flag & VR_PROT_MASK) == VR_PROT_NONE) || + !(range->flag & VR_PROT_WRITE)){ + ihk_mc_spinlock_unlock_noirq(&vm->memory_range_lock); + return -EFAULT; + } + ihk_mc_spinlock_unlock_noirq(&vm->memory_range_lock); + *dst = data; + return 0; +} + +int setint_user(int *dst, int data) +{ + struct process_vm *vm = cpu_local_var(current)->vm; + struct vm_range *range; + unsigned long pint = (unsigned long)dst; + unsigned long pgstart; + + pgstart = pint & 0xfffffffffffff000UL; + if(!pgstart || pgstart >= MAP_KERNEL_START) + return -EFAULT; + ihk_mc_spinlock_lock_noirq(&vm->memory_range_lock); + range = lookup_process_memory_range(vm, pint, pint + sizeof(int)); + if(range == NULL){ + ihk_mc_spinlock_unlock_noirq(&vm->memory_range_lock); + return -EFAULT; + } + if(((range->flag & VR_PROT_MASK) == VR_PROT_NONE) || + !(range->flag & VR_PROT_WRITE)){ + ihk_mc_spinlock_unlock_noirq(&vm->memory_range_lock); + return -EFAULT; + } + ihk_mc_spinlock_unlock_noirq(&vm->memory_range_lock); + *dst = data; + return 0; +} + int write_process_vm(struct process_vm *vm, void *udst, const void *ksrc, size_t siz) { const uintptr_t ustart = (uintptr_t)udst; diff --git a/kernel/syscall.c b/kernel/syscall.c index e17c7059..b2c89a9d 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -1660,14 +1660,14 @@ SYSCALL_DECLARE(execve) } /* Flatten argv and envp into kernel-space buffers */ - argv_flat_len = flatten_strings(-1, (desc->shell_path[0] ? + argv_flat_len = flatten_strings_from_user(-1, (desc->shell_path[0] ? desc->shell_path : NULL), argv, &argv_flat); if (argv_flat_len == 0) { kprintf("ERROR: no argv for executable: %s?\n", filename); return -EINVAL; } - envp_flat_len = flatten_strings(-1, NULL, envp, &envp_flat); + envp_flat_len = flatten_strings_from_user(-1, NULL, envp, &envp_flat); if (envp_flat_len == 0) { kprintf("ERROR: no envp for executable: %s?\n", filename); return -EINVAL; diff --git a/lib/include/string.h b/lib/include/string.h index 0b1bb490..08aa484e 100644 --- a/lib/include/string.h +++ b/lib/include/string.h @@ -31,5 +31,6 @@ void *memset(void *s, int n, size_t l); unsigned long strtol(const char *cp, char **endp, unsigned int base); int flatten_strings(int nr_strings, char *first, char **strings, char **flat); +int flatten_strings_from_user(int nr_strings, char *first, char **strings, char **flat); #endif diff --git a/lib/string.c b/lib/string.c index 2e9bfb2e..55a93f7a 100644 --- a/lib/string.c +++ b/lib/string.c @@ -13,6 +13,10 @@ #include #include +extern int strlen_user(const char *); +extern int strcpy_from_user(char *, const char *); +extern long getlong_user(const void *); + size_t strlen(const char *p) { const char *head = p; @@ -268,3 +272,70 @@ int flatten_strings(int nr_strings, char *first, char **strings, char **flat) return full_len; } +int flatten_strings_from_user(int nr_strings, char *first, char **strings, char **flat) +{ + int full_len, string_i; + long *_flat; + char *p; + long r; + int n; + + /* How many strings do we have? */ + if (nr_strings == -1) { + for (nr_strings = 0; (r = getlong_user(strings + nr_strings)) > 0; ++nr_strings); + if(r < 0) + return r; + } + + /* Count full length */ + full_len = sizeof(long) + sizeof(char *); // Counter and terminating NULL + if (first) { + int len = strlen(first); + + if(len < 0) + return len; + full_len += sizeof(char *) + len + 1; + } + + for (string_i = 0; string_i < nr_strings; ++string_i) { + char *userp = (char *)getlong_user(strings + string_i); + int len = strlen_user(userp); + + if(len < 0) + return len; + // Pointer + actual value + full_len += sizeof(char *) + len + 1; + } + + full_len = (full_len + sizeof(long) - 1) & ~(sizeof(long) - 1); + + _flat = kmalloc(full_len, IHK_MC_AP_NOWAIT); + if (!_flat) { + return -ENOMEM; + } + + /* Number of strings */ + n = first? 1: 0; + _flat[0] = nr_strings + n; + + // Actual offset + p = (char *)(_flat + nr_strings + 2 + n); + + n = 1; + if (first) { + _flat[n++] = p - (char *)_flat; + strcpy(p, first); + p = strchr(p, '\0') + 1; + } + + for (string_i = 0; string_i < nr_strings; ++string_i) { + char *userp = (char *)getlong_user(strings + string_i); + _flat[n++] = p - (char *)_flat; + strcpy_from_user(p, userp); + p = strchr(p, '\0') + 1; + } + _flat[n] = 0; + + *flat = (char *)_flat; + return full_len; +}