From a9e6aabcaedc4daedf7c22dd7ee5d1de99c56f24 Mon Sep 17 00:00:00 2001 From: Bahadir Balban Date: Tue, 25 Nov 2008 11:05:41 +0200 Subject: [PATCH] We can now successfully copy char *argv[] user array. After a lot of magic pointer arithmetic and mapping. --- tasks/mm0/include/memory.h | 2 + tasks/mm0/src/execve.c | 192 ++++++++++++++++++++++++++++++------- tasks/mm0/src/fault.c | 6 +- tasks/mm0/src/memory.c | 49 ++++++++++ tasks/mm0/src/user.c | 4 +- tasks/test0/src/exectest.c | 10 +- 6 files changed, 223 insertions(+), 40 deletions(-) diff --git a/tasks/mm0/include/memory.h b/tasks/mm0/include/memory.h index 4607ccb..efd5b75 100644 --- a/tasks/mm0/include/memory.h +++ b/tasks/mm0/include/memory.h @@ -30,4 +30,6 @@ void *pager_map_page(struct vm_file *f, unsigned long page_offset); void pager_unmap_page(void *addr); void *pager_map_file_range(struct vm_file *f, unsigned long byte_offset, unsigned long size); +void *pager_validate_map_user_range2(struct tcb *user, void *userptr, + unsigned long size, unsigned int vm_flags); #endif /* __MEMORY_H__ */ diff --git a/tasks/mm0/src/execve.c b/tasks/mm0/src/execve.c index 42bc2ec..19edc99 100644 --- a/tasks/mm0/src/execve.c +++ b/tasks/mm0/src/execve.c @@ -104,20 +104,13 @@ int do_execve(struct tcb *sender, char *filename) return (int)new_task; } - /* Fill in tcb memory segment markers from executable file */ + /* Fill and validate tcb memory segment markers from executable file */ if ((err = task_setup_from_executable(vmfile, new_task, &efd)) < 0) { vm_file_put(vmfile); kfree(new_task); return err; } - /* Map task segment markers as virtual memory regions */ - if ((err = task_mmap_segments(new_task, vmfile, &efd)) < 0) { - vm_file_put(vmfile); - kfree(new_task); - return err; - } - /* * If sender is a thread in a group, need to find the * group leader and destroy all threaded children in @@ -152,6 +145,13 @@ int do_execve(struct tcb *sender, char *filename) */ do_exit(tgleader, EXIT_UNMAP_ALL_SPACE, 0); + /* Map task's new segment markers as virtual memory regions */ + if ((err = task_mmap_segments(new_task, vmfile, &efd)) < 0) { + vm_file_put(vmfile); + kfree(new_task); + return err; + } + /* Set up task registers via exchange_registers() */ task_setup_registers(new_task, 0, 0, new_task->pagerid); @@ -189,16 +189,6 @@ Dynamic Linking. } -/* - * Copies a null-terminated ragged array (i.e. argv[0]) from userspace into - * buffer. If any page boundary is hit, unmaps previous page, validates and - * maps the new page. - */ -int copy_user_ragged(struct tcb *task, char *buf[], char *user[], int maxlength) -{ - return 0; -} - /* * Copy from one buffer to another. Stop if maxlength or * a page boundary is hit. @@ -208,9 +198,11 @@ int strncpy_page(char *to, char *from, int maxlength) int count = 0; do { - if ((to[count] = from[count]) == '\0') + if ((to[count] = from[count]) == '\0') { + count++; break; - count++; + } else + count++; } while (count < maxlength && !page_boundary(&from[count])); if (page_boundary(&from[count])) @@ -218,19 +210,18 @@ int strncpy_page(char *to, char *from, int maxlength) if (count == maxlength) return -E2BIG; - return 0; + return count; } /* * Copies a userspace string into buffer. If a page boundary is hit, - * unmaps the previous page, validates and maps the new page + * unmaps the previous page, validates and maps the new page. */ int copy_user_string(struct tcb *task, char *buf, char *user, int maxlength) { int count = maxlength; + int copied = 0, ret = 0, total = 0; char *mapped = 0; - int copied = 0; - int err = 0; /* Map the first page the user buffer is in */ if (!(mapped = pager_validate_map_user_range(task, user, @@ -238,10 +229,10 @@ int copy_user_string(struct tcb *task, char *buf, char *user, int maxlength) VM_READ))) return -EINVAL; - while ((err = strncpy_page(&buf[copied], mapped, count)) < 0) { - if (err == -E2BIG) - return err; - if (err == -EFAULT) { + while ((ret = strncpy_page(&buf[copied], mapped, count)) < 0) { + if (ret == -E2BIG) + return ret; + else if (ret == -EFAULT) { pager_unmap_user_range(mapped, TILL_PAGE_ENDS(mapped)); copied += TILL_PAGE_ENDS(mapped); count -= TILL_PAGE_ENDS(mapped); @@ -252,25 +243,158 @@ int copy_user_string(struct tcb *task, char *buf, char *user, int maxlength) return -EINVAL; } } + total = copied + ret; /* Unmap the final page */ pager_unmap_user_range(mapped, TILL_PAGE_ENDS(mapped)); - return 0; + return total; } +/* + * Copy from one buffer to another. Stop if maxlength or + * a page boundary is hit. Breaks if unsigned long sized copy value is 0, + * as opposed to a 0 byte as in string copy. + */ +int bufncpy_page(unsigned long *to, unsigned long *from, int maxlength) +{ + int count = 0; + + do { + if ((to[count] = from[count]) == 0) { + count++; + break; + } else + count++; + } while (count < maxlength && !page_boundary(&from[count])); + + if (page_boundary(&from[count])) + return -EFAULT; + if (count == maxlength) + return -E2BIG; + + return count; +} + +/* + * Copies a userspace string into buffer. If a page boundary is hit, + * unmaps the previous page, validates and maps the new page. + */ +int copy_user_buf(struct tcb *task, char *buf, char *user, int maxlength) +{ + int count = maxlength; + int copied = 0, ret = 0, total = 0; + unsigned long *mapped = 0; + + /* Map the first page the user buffer is in */ + if (!(mapped = pager_validate_map_user_range(task, user, + TILL_PAGE_ENDS(user), + VM_READ))) + return -EINVAL; + + while ((ret = bufncpy_page((unsigned long *)&buf[copied], mapped, count)) < 0) { + if (ret == -E2BIG) + return ret; + else if (ret == -EFAULT) { + pager_unmap_user_range(mapped, TILL_PAGE_ENDS(mapped)); + copied += TILL_PAGE_ENDS(mapped); + count -= TILL_PAGE_ENDS(mapped); + if (!(mapped = + pager_validate_map_user_range(task, user + copied, + TILL_PAGE_ENDS(user + copied), + VM_READ))) + return -EINVAL; + } + } + total = copied + ret; + + /* Unmap the final page */ + pager_unmap_user_range(mapped, TILL_PAGE_ENDS(mapped)); + + return total; +} + +struct args_struct { + int argc; + char **argv; + int size; /* Size of strings + string pointers */ +}; + +int copy_user_args(struct tcb *task, struct args_struct *args, + void *argv_user, int args_max) +{ + char **argv = 0; + void *argsbuf; + char *curbuf; + int argc = 0; + int used; + int count; + + if (!(argsbuf = kzalloc(args_max))) + return -ENOMEM; + + /* + * First, copy the null-terminated array of + * pointers to argument strings. + */ + if ((count = copy_user_buf(task, argsbuf, argv_user, args_max)) < 0) + goto out; + + /* On success, we get the number of arg strings + the terminator */ + argc = count - 1; + used = count * sizeof(char *); + argv = argsbuf; + curbuf = argsbuf + used; + + /* Now we copy each argument string into buffer */ + for (int i = 0; i < argc; i++) { + /* Copy string into empty space in buffer */ + if ((count = copy_user_string(task, curbuf, argv[i], + args_max - used)) < 0) + goto out; + + /* Replace pointer to string with copied location */ + argv[i] = curbuf; + + /* Update current empty buffer location */ + curbuf += count; + + /* Increase used buffer count */ + used += count; + } + + /* Set up the args struct */ + args->argc = argc; + args->argv = argv; + args->size = used; + + return 0; + +out: + kfree(argsbuf); + return count; +} + + int sys_execve(struct tcb *sender, char *pathname, char *argv[], char *envp[]) { - int err; + int ret; char *path = kzalloc(PATH_MAX); + struct args_struct args; /* Copy the executable path string */ - if ((err = copy_user_string(sender, path, pathname, PATH_MAX)) < 0) - return err; + if ((ret = copy_user_string(sender, path, pathname, PATH_MAX)) < 0) + return ret; printf("%s: Copied pathname: %s\n", __FUNCTION__, path); - return do_execve(sender, path); + /* Copy the env and args */ + if ((ret = copy_user_args(sender, &args, argv, ARGS_MAX)) < 0) + return ret; + + ret = do_execve(sender, path); + kfree(path); + + return ret; } - diff --git a/tasks/mm0/src/fault.c b/tasks/mm0/src/fault.c index d06b110..c139cd8 100644 --- a/tasks/mm0/src/fault.c +++ b/tasks/mm0/src/fault.c @@ -896,8 +896,8 @@ struct page *task_virt_to_page(struct tcb *t, unsigned long virtual) /* First find the vma that maps that virtual address */ if (!(vma = find_vma(virtual, &t->vm_area_head->list))) { - printf("%s: No VMA found for 0x%x on task: %d\n", - __FUNCTION__, virtual, t->tid); + //printf("%s: No VMA found for 0x%x on task: %d\n", + // __FUNCTION__, virtual, t->tid); return PTR_ERR(-EINVAL); } @@ -923,7 +923,7 @@ struct page *task_virt_to_page(struct tcb *t, unsigned long virtual) &vma->vm_obj_list))) { printf("%s:%s: Traversed all shadows and the original " "file's vm_object, but could not find the " - "faulty page in this vma.\n",__TASKNAME__, + "page in this vma.\n",__TASKNAME__, __FUNCTION__); BUG(); } diff --git a/tasks/mm0/src/memory.c b/tasks/mm0/src/memory.c index 8584e74..f06e2ef 100644 --- a/tasks/mm0/src/memory.c +++ b/tasks/mm0/src/memory.c @@ -15,6 +15,7 @@ #include INC_SUBARCH(mm.h) #include #include +#include struct membank membank[1]; struct page *page_array; @@ -193,3 +194,51 @@ void *pager_map_file_range(struct vm_file *f, unsigned long byte_offset, return (void *)((unsigned long)page | (PAGE_MASK & byte_offset)); } +void *pager_validate_map_user_range2(struct tcb *user, void *userptr, + unsigned long size, unsigned int vm_flags) +{ + unsigned long start = page_align(userptr); + unsigned long end = page_align_up(userptr + size); + unsigned long npages = end - start; + void *virt, *virt_start; + void *mapped = 0; + + /* Validate that user task owns this address range */ + if (pager_validate_user_range(user, userptr, size, vm_flags) < 0) + return 0; + + /* Get the address range */ + if (!(virt_start = pager_new_address(npages))) + return PTR_ERR(-ENOMEM); + virt = virt_start; + + /* Map every page contiguously in the allocated virtual address range */ + for (unsigned long addr = start; addr < end; addr += PAGE_SIZE) { + struct page *p = task_virt_to_page(user, addr); + + if (IS_ERR(p)) { + /* Unmap pages mapped so far */ + l4_unmap_helper(virt_start, __pfn(addr - start)); + + /* Delete virtual address range */ + pager_delete_address(virt_start, npages); + + return p; + } + + l4_map((void *)page_to_phys(task_virt_to_page(user, addr)), + virt, 1, MAP_USR_RW_FLAGS, self_tid()); + virt += PAGE_SIZE; + } + + /* Set the mapped pointer to offset of user pointer given */ + mapped = virt_start; + mapped = (void *)(((unsigned long)mapped) | + ((unsigned long)(PAGE_MASK & + (unsigned long)userptr))); + + /* Return the mapped pointer */ + return mapped; +} + + diff --git a/tasks/mm0/src/user.c b/tasks/mm0/src/user.c index f33e78e..b3a7d32 100644 --- a/tasks/mm0/src/user.c +++ b/tasks/mm0/src/user.c @@ -25,8 +25,8 @@ int pager_validate_user_range(struct tcb *user, void *userptr, unsigned long siz /* Find the vma that maps that virtual address */ for (unsigned long vaddr = start; vaddr < end; vaddr += PAGE_SIZE) { if (!(vma = find_vma(vaddr, &user->vm_area_head->list))) { - printf("%s: No VMA found for 0x%x on task: %d\n", - __FUNCTION__, vaddr, user->tid); + //printf("%s: No VMA found for 0x%x on task: %d\n", + // __FUNCTION__, vaddr, user->tid); return -1; } if ((vma->flags & vmflags) != vmflags) diff --git a/tasks/test0/src/exectest.c b/tasks/test0/src/exectest.c index db83d47..1c0dd87 100644 --- a/tasks/test0/src/exectest.c +++ b/tasks/test0/src/exectest.c @@ -20,6 +20,7 @@ int exectest(void) void *exec_start = (void *)_start_test1; unsigned long size = _end_test1 - _start_test1; int left, cnt; + char *argv[5]; /* First create a new file and write the executable data to that file */ printf("%s: Creating new executable file.\n", __FUNCTION__); @@ -41,9 +42,16 @@ int exectest(void) close(fd); + /* Set up some arguments */ + argv[0] = "FIRST ARG"; + argv[1] = "SECOND ARG"; + argv[2] = "THIRD ARG"; + argv[3] = "FOURTH ARG"; + argv[4] = 0; + printf("%s: Executing the file.\n", __FUNCTION__); /* Execute the file */ - execve("/home/bahadir/test1.axf", 0, 0); + execve("/home/bahadir/test1.axf", argv, 0); return 0; }