From cf4bf90cbb06eaa90961bdeab41f83a99666a90a Mon Sep 17 00:00:00 2001 From: Gleb Koval Date: Tue, 12 Nov 2024 15:34:45 +0000 Subject: [PATCH] Implement user pointer checking for C strings --- src/userprog/syscall.c | 74 ++++++++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/src/userprog/syscall.c b/src/userprog/syscall.c index ca6f116..2b0a551 100644 --- a/src/userprog/syscall.c +++ b/src/userprog/syscall.c @@ -14,6 +14,8 @@ #include #include +#define MAX_SYSCALL_ARGS 3 + static struct lock filesys_lock; static unsigned fd_counter = MIN_USER_FD; @@ -47,8 +49,9 @@ static unsigned syscall_tell (int fd); static void syscall_close (int fd); static struct open_file *fd_get_file (int fd); -static void *validate_user_pointer (const void *ptr, size_t size, - bool check_write); +static void validate_user_pointer (const void *ptr, size_t size, + bool check_write); +static void validate_user_string (const char *str, bool check_write); static int get_user (const uint8_t *); static bool put_user (uint8_t *, uint8_t); @@ -100,8 +103,8 @@ static void syscall_handler (struct intr_frame *f) { /* First, read the system call number from the stack. */ - validate_user_pointer (f->esp, 1, false); - unsigned syscall_number = *(int *)f->esp; + validate_user_pointer (f->esp, sizeof (uintptr_t), false); + uintptr_t syscall_number = *(int *)f->esp; /* Ensures the number corresponds to a system call that can be handled. */ if (syscall_number >= LOOKUP_SIZE) @@ -112,8 +115,8 @@ syscall_handler (struct intr_frame *f) /* Next, read and copy the arguments from the stack pointer. */ validate_user_pointer (f->esp + sizeof (uintptr_t), syscall.arity * sizeof (uintptr_t), false); - uintptr_t args[3] = { 0 }; - for (int i = 0; i < syscall.arity; i++) + uintptr_t args[MAX_SYSCALL_ARGS] = { 0 }; + for (int i = 0; i < syscall.arity && i < MAX_SYSCALL_ARGS; i++) args[i] = *(uintptr_t *)(f->esp + sizeof (uintptr_t) * (i + 1)); /* Call the function that handles this system call with the arguments. When @@ -145,7 +148,7 @@ syscall_exit (int status) static pid_t syscall_exec (const char *cmd_line) { - validate_user_pointer (cmd_line, 1, false); + validate_user_string (cmd_line, false); lock_acquire (&filesys_lock); pid_t pid = process_execute(cmd_line); @@ -168,7 +171,7 @@ syscall_wait (pid_t pid) static bool syscall_create (const char *file UNUSED, unsigned initial_size UNUSED) { - validate_user_pointer (file, 1, false); + validate_user_string (file, false); lock_acquire (&filesys_lock); bool status = filesys_create (file, initial_size); @@ -183,7 +186,7 @@ syscall_create (const char *file UNUSED, unsigned initial_size UNUSED) static bool syscall_remove (const char *file) { - validate_user_pointer (file, 1, false); + validate_user_string (file, false); lock_acquire (&filesys_lock); bool status = filesys_remove (file); @@ -199,7 +202,7 @@ syscall_remove (const char *file) static int syscall_open (const char *file) { - validate_user_pointer (file, 1, false); + validate_user_string (file, false); lock_acquire (&filesys_lock); struct file *ptr = filesys_open (file); @@ -405,26 +408,49 @@ fd_get_file (int fd) } /* Validates if a block of memory starting at PTR and of size SIZE bytes is - fully contained within user virtual memory. Returns NULL if the memory - is invalid. Otherwise, returns the PTR given. - If the size is 0, the function does no checks and returns PTR.*/ -static void * + fully contained within valid user virtual memory. thread_exit () if the + memory is invalid. + If the size is 0, the function does no checks and returns PTR. */ +static void validate_user_pointer (const void *ptr, size_t size, bool check_write) { if (size == 0) - return ptr; + return; /* ptr < ptr + size - 1, so sufficient to check that (ptr + size -1) is a valid user virtual memory address. */ - if (!is_user_vaddr (ptr + size - 1)) + void *last = ptr + size - 1; + if (!is_user_vaddr (last)) thread_exit (); - /* Check read access to pointer. */ - int result; - if ((result = get_user (ptr)) == -1) - thread_exit (); - /* Check write access to pointer (if required). */ - if (check_write && !put_user (ptr, result)) - thread_exit (); - return ptr; + for (; ptr <= last; ptr++) + { + int result; + /* Check read access to pointer. */ + if ((result = get_user (ptr)) == -1) + thread_exit (); + /* Check write access to pointer (if required). */ + if (check_write && !put_user (ptr, result)) + thread_exit (); + } +} + +/* Validates of a C-string starting at ptr is fully contained within valid + user virtual memory. thread_exit () if the memory is invalid. */ +static void +validate_user_string (const char *ptr, bool check_write) +{ + while (true) + { + if (!is_user_vaddr (ptr)) + thread_exit (); + int result; + if ((result = get_user ((const uint8_t *)ptr)) == -1) + thread_exit (); + if (check_write && !put_user ((uint8_t *)ptr, result)) + thread_exit (); + if (*ptr == '\0') + return; + ptr++; + } } /* PROVIDED BY SPEC.