From 0cd5091132a49cd38121db989098879be3f26245 Mon Sep 17 00:00:00 2001 From: Bahadir Balban Date: Tue, 13 Oct 2009 19:11:14 +0300 Subject: [PATCH] Moved user buffer access functions to user.c --- conts/posix/mm0/include/user.h | 8 + conts/posix/mm0/mm/execve.c | 186 ---------------------- conts/posix/mm0/mm/user.c | 276 +++++++++++++++++++++++++++++++++ conts/posix/test0/src/fileio.c | 30 +++- 4 files changed, 308 insertions(+), 192 deletions(-) diff --git a/conts/posix/mm0/include/user.h b/conts/posix/mm0/include/user.h index fc0206b..e91e2e4 100644 --- a/conts/posix/mm0/include/user.h +++ b/conts/posix/mm0/include/user.h @@ -9,4 +9,12 @@ void *pager_validate_map_user_range(struct tcb *user, void *userptr, unsigned long size, unsigned int vm_flags); void pager_unmap_user_range(void *mapped_ptr, unsigned long size); +int copy_user_args(struct tcb *task, struct args_struct *args, + void *argv_user, int args_max); +int copy_user_buf(struct tcb *task, void *buf, char *user, int maxlength, + int elem_size); +int copy_user_string(struct tcb *task, void *buf, char *user, int maxlength); +int copy_to_user(struct tcb *task, char *user, void *buf, int size); +int copy_from_user(struct tcb *task, void *buf, char *user, int size); + #endif /* __USER_H__ */ diff --git a/conts/posix/mm0/mm/execve.c b/conts/posix/mm0/mm/execve.c index a200e90..770823d 100644 --- a/conts/posix/mm0/mm/execve.c +++ b/conts/posix/mm0/mm/execve.c @@ -221,192 +221,6 @@ Dynamic Linking. return 0; } -/* - * Copy from one buffer to another. Stop if maxlength or - * a page boundary is hit. - */ -int strncpy_page(void *to_ptr, void *from_ptr, int maxlength) -{ - int count = 0; - char *to = to_ptr, *from = from_ptr; - - 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; -} - -/* - * 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. If byte size 0 was used - * a valid pointer with a 0 byte in it would give a false termination. - */ -int bufncpy_page(void *to_ptr, void *from_ptr, int maxlength) -{ - int count = 0; - unsigned long *to = to_ptr, *from = from_ptr; - - 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 variable sized userspace string or array of pointers - * (think &argv[0]), 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, void *buf, char *user, int maxlength, - int elem_size) -{ - int count = maxlength; - int copied = 0, ret = 0, total = 0; - void *mapped = 0; - int (*copy_func)(void *, void *, int count); - - /* This bit determines what size copier function to use. */ - if (elem_size == sizeof(char)) - copy_func = strncpy_page; - else if (elem_size == sizeof(unsigned long)) - copy_func = bufncpy_page; - else - return -EINVAL; - - /* 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 = copy_func(buf + copied, mapped, count)) < 0) { - if (ret == -E2BIG) - return ret; - else if (ret == -EFAULT) { - /* - * Copied is always in bytes no matter what elem_size is - * because we know we hit a page boundary and we increase - * by the page boundary bytes - */ - 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; - } - } - - /* Note copied is always in bytes */ - total = (copied / elem_size) + ret; - - /* Unmap the final page */ - pager_unmap_user_range(mapped, TILL_PAGE_ENDS(mapped)); - - return total; -} - -/* - * Calls copy_user_buf with char-sized copying. This matters because - * buffer is variable and the terminator must be in char size - */ -static inline int -copy_user_string(struct tcb *task, void *buf, char *user, - int maxlength) -{ - return copy_user_buf(task, buf, user, maxlength, sizeof(char)); -} - -/* - * Calls copy_user_buf with unsigned long sized copying. This matters - * because buffer is variable and the terminator must be in ulong size - */ -static inline int -copy_user_ptrs(struct tcb *task, void *buf, char *user, - int maxlength) -{ - return copy_user_buf(task, buf, user, maxlength, sizeof(unsigned long)); -} - - -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_ptrs(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 ret; diff --git a/conts/posix/mm0/mm/user.c b/conts/posix/mm0/mm/user.c index b3a7d32..530f3de 100644 --- a/conts/posix/mm0/mm/user.c +++ b/conts/posix/mm0/mm/user.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include /* * Checks if the given user virtual address range is @@ -74,3 +76,277 @@ void pager_unmap_user_range(void *mapped_ptr, unsigned long size) __pfn(page_align_up(size))); } +/* + * Copy from one buffer to another. Stop if maxlength or + * a page boundary is hit. + */ +int strncpy_page(void *to_ptr, void *from_ptr, int maxlength) +{ + int count = 0; + char *to = to_ptr, *from = from_ptr; + + 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; +} + +/* + * 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. If byte size 0 was used + * a valid pointer with a 0 byte in it would give a false termination. + */ +int bufncpy_page(void *to_ptr, void *from_ptr, int maxlength) +{ + int count = 0; + unsigned long *to = to_ptr, *from = from_ptr; + + 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 src to dest for given size, return -EFAULT on page boundaries. + */ +int memcpy_page(void *dst, void *src, int size, int fault_on_dest) +{ + int count = 0; + char *to = dst, *from = src; + + if (!fault_on_dest) { + do { + to[count] = from[count]; + count++; + } while (count < size && + !page_boundary(&from[count])); + } else { + do { + to[count] = from[count]; + count++; + } while (count < size && + !page_boundary(&to[count])); + } + + if (page_boundary(&from[count])) + return -EFAULT; + + return count; +} + +int copy_from_user(struct tcb *task, void *buf, char *user, int size) +{ + int copied = 0, ret = 0, total = 0; + int count = size; + void *mapped = 0; + + if (!(mapped = pager_validate_map_user_range(task, user, + TILL_PAGE_ENDS(user), + VM_READ))) + return -EINVAL; + + while ((ret = memcpy_page(buf + copied, mapped, count, 0)) < 0) { + 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; + } + + /* Note copied is always in bytes */ + total = copied + ret; + + /* Unmap the final page */ + pager_unmap_user_range(mapped, TILL_PAGE_ENDS(mapped)); + + return total; +} + +int copy_to_user(struct tcb *task, char *user, void *buf, int size) +{ + int copied = 0, ret = 0, total = 0; + int count = size; + void *mapped = 0; + + /* Map the user page */ + if (!(mapped = pager_validate_map_user_range(task, user, + TILL_PAGE_ENDS(user), + VM_READ))) + return -EINVAL; + + while ((ret = memcpy_page(mapped, buf + copied, count, 1)) < 0) { + 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; + } + + /* Note copied is always in bytes */ + total = copied + ret; + + /* Unmap the final page */ + pager_unmap_user_range(mapped, TILL_PAGE_ENDS(mapped)); + + return total; +} + +/* + * Copies a variable sized userspace string or array of pointers + * (think &argv[0]), 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, void *buf, char *user, int maxlength, + int elem_size) +{ + int count = maxlength; + int copied = 0, ret = 0, total = 0; + void *mapped = 0; + int (*copy_func)(void *, void *, int count); + + /* This bit determines what size copier function to use. */ + if (elem_size == sizeof(char)) + copy_func = strncpy_page; + else if (elem_size == sizeof(unsigned long)) + copy_func = bufncpy_page; + else + return -EINVAL; + + /* 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 = copy_func(buf + copied, mapped, count)) < 0) { + if (ret == -E2BIG) + return ret; + else if (ret == -EFAULT) { + /* + * Copied is always in bytes no matter what elem_size is + * because we know we hit a page boundary and we increase + * by the page boundary bytes + */ + 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; + } + } + + /* Note copied is always in bytes */ + total = (copied / elem_size) + ret; + + /* Unmap the final page */ + pager_unmap_user_range(mapped, TILL_PAGE_ENDS(mapped)); + + return total; +} + +/* + * Calls copy_user_buf with char-sized copying. This matters because + * buffer is variable and the terminator must be in char size + */ +int copy_user_string(struct tcb *task, void *buf, char *user, int maxlength) +{ + return copy_user_buf(task, buf, user, maxlength, sizeof(char)); +} + +/* + * Calls copy_user_buf with unsigned long sized copying. This matters + * because buffer is variable and the terminator must be in ulong size + */ +static inline int +copy_user_ptrs(struct tcb *task, void *buf, char *user, + int maxlength) +{ + return copy_user_buf(task, buf, user, maxlength, sizeof(unsigned long)); +} + +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_ptrs(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; +} + diff --git a/conts/posix/test0/src/fileio.c b/conts/posix/test0/src/fileio.c index f5b398c..fd7ee56 100644 --- a/conts/posix/test0/src/fileio.c +++ b/conts/posix/test0/src/fileio.c @@ -7,25 +7,27 @@ #include #include #include - -#define PAGE_SIZE 0x1000 +#include INC_GLUE(memory.h) int small_io_test(void) { int fd1, fd2; char *string = "abcdefg"; char strbuf[30]; + char strbuf2[30]; char *path = "/text.txt"; + //char stackbuf[PAGE_SIZE*2]; + fd1 = open(path, O_TRUNC | O_RDWR | O_CREAT, S_IRWXU); fd2 = open(path, O_RDWR, 0); - printf("fd1: %d, fd2: %d\n", fd1, fd2); + test_printf("%s: fd1: %d, fd2: %d\n", __FUNCTION__, fd1, fd2); perror("OPEN"); for (int i = 0; i < 4; i++) { sprintf(strbuf, "%s%d", string, i); - printf("Writing to %s offset %x, string: %s\n", - path, i*PAGE_SIZE, strbuf); + test_printf("Writing to %s offset %x, string: %s\n", + path, i*PAGE_SIZE, strbuf); lseek(fd1, i*PAGE_SIZE, SEEK_SET); write(fd1, strbuf, strlen(strbuf) + 1); } @@ -33,13 +35,29 @@ int small_io_test(void) memset(strbuf, 0, 30); for (int i = 0; i < 4; i++) { + sprintf(strbuf2, "%s%d", string, i); lseek(fd2, i*PAGE_SIZE, SEEK_SET); read(fd2, strbuf, 30); - printf("Read %s, offset %x as %s\n", path, i*PAGE_SIZE, strbuf); + test_printf("Read %s, offset %x as %s\n", + path, i*PAGE_SIZE, strbuf); + if (strcmp(strbuf, strbuf2)) + goto out_err; } +#if 0 + /* Now read into an unaligned buffer larger than page size */ + lseek(fd2, 0, SEEK_SET); + read(fd2, stackbuf, PAGE_SIZE * 2); + printf("stackbuf beginning: %s\n second page beginning: %s\n", + stackbuf, &stackbuf[PAGE_SIZE]); +#endif close(fd2); + printf("MINI IO TEST -- PASSED --\n"); + return 0; + +out_err: + printf("MINI IO TEST -- FAILED --\n"); return 0; }