From 30e49846b5f5f93f73e6aa329033eb658bac276a Mon Sep 17 00:00:00 2001 From: sBubshait Date: Wed, 13 Nov 2024 16:21:12 +0000 Subject: [PATCH 01/11] Add more tests for system calls to deal with bad buffers given to read and write --- src/tests/userprog/Make.tests | 11 ++++++++--- src/tests/userprog/Rubric.robustnessCR | 8 ++++++-- src/tests/userprog/exec-bad-ptr.ck | 6 +----- src/tests/userprog/open-bad-ptr.ck | 6 +----- src/tests/userprog/overflow-stack.c | 17 +++++++++++++++++ src/tests/userprog/overflow-stack.ck | 14 ++++++++++++++ src/tests/userprog/read-bad-buf.c | 17 +++++++++++++++++ src/tests/userprog/read-bad-buf.ck | 10 ++++++++++ src/tests/userprog/read-bad-ptr.ck | 7 +------ src/tests/userprog/write-bad-buf.c | 17 +++++++++++++++++ src/tests/userprog/write-bad-buf.ck | 10 ++++++++++ src/tests/userprog/write-bad-ptr.ck | 7 +------ 12 files changed, 103 insertions(+), 27 deletions(-) create mode 100644 src/tests/userprog/overflow-stack.c create mode 100644 src/tests/userprog/overflow-stack.ck create mode 100644 src/tests/userprog/read-bad-buf.c create mode 100644 src/tests/userprog/read-bad-buf.ck create mode 100644 src/tests/userprog/write-bad-buf.c create mode 100644 src/tests/userprog/write-bad-buf.ck diff --git a/src/tests/userprog/Make.tests b/src/tests/userprog/Make.tests index 02c73fe..542ea0b 100644 --- a/src/tests/userprog/Make.tests +++ b/src/tests/userprog/Make.tests @@ -9,14 +9,14 @@ sc-bad-arg sc-bad-num sc-boundary sc-boundary-2 halt exit create-normal \ create-empty create-null create-bad-ptr create-long create-exists \ create-bound open-normal open-missing open-boundary open-empty \ open-null open-bad-ptr open-twice close-normal close-twice close-stdin \ -close-stdout close-bad-fd read-normal read-bad-ptr read-boundary \ -read-zero read-stdout read-bad-fd write-normal write-bad-ptr \ +close-stdout close-bad-fd read-normal read-bad-ptr read-bad-buf read-boundary \ +read-zero read-stdout read-bad-fd write-normal write-bad-ptr write-bad-buf \ write-boundary write-zero write-stdin write-bad-fd exec-once exec-arg \ exec-large-arg exec-multiple exec-missing exec-over-arg exec-over-args \ exec-bad-ptr wait-simple wait-twice wait-killed wait-load-kill \ wait-bad-pid wait-bad-child multi-recurse multi-child-fd rox-simple \ rox-child rox-multichild bad-read bad-write bad-read2 bad-write2 \ -bad-jump bad-jump2 bad-maths) +bad-jump bad-jump2 bad-maths overflow-stack) tests/userprog_PROGS = $(tests/userprog_TESTS) $(addprefix \ tests/userprog/,child-simple child-args child-bad child-close child-rox exec-exit) @@ -36,6 +36,7 @@ tests/userprog/bad-read2_SRC = tests/userprog/bad-read2.c tests/main.c tests/userprog/bad-write2_SRC = tests/userprog/bad-write2.c tests/main.c tests/userprog/bad-jump2_SRC = tests/userprog/bad-jump2.c tests/main.c tests/userprog/bad-maths_SRC = tests/userprog/bad-maths.c tests/main.c +tests/userprog/overflow-stack_SRC = tests/userprog/overflow-stack.c tests/main.c tests/userprog/sc-boundary_SRC = tests/userprog/sc-boundary.c \ tests/userprog/boundary.c tests/main.c tests/userprog/sc-boundary-2_SRC = tests/userprog/sc-boundary-2.c \ @@ -66,6 +67,7 @@ tests/userprog/close-stdout_SRC = tests/userprog/close-stdout.c tests/main.c tests/userprog/close-bad-fd_SRC = tests/userprog/close-bad-fd.c tests/main.c tests/userprog/read-normal_SRC = tests/userprog/read-normal.c tests/main.c tests/userprog/read-bad-ptr_SRC = tests/userprog/read-bad-ptr.c tests/main.c +tests/userprog/read-bad-buf_SRC = tests/userprog/read-bad-buf.c tests/main.c tests/userprog/read-boundary_SRC = tests/userprog/read-boundary.c \ tests/userprog/boundary.c tests/main.c tests/userprog/read-zero_SRC = tests/userprog/read-zero.c tests/main.c @@ -73,6 +75,7 @@ tests/userprog/read-stdout_SRC = tests/userprog/read-stdout.c tests/main.c tests/userprog/read-bad-fd_SRC = tests/userprog/read-bad-fd.c tests/main.c tests/userprog/write-normal_SRC = tests/userprog/write-normal.c tests/main.c tests/userprog/write-bad-ptr_SRC = tests/userprog/write-bad-ptr.c tests/main.c +tests/userprog/write-bad-buf_SRC = tests/userprog/write-bad-buf.c tests/main.c tests/userprog/write-boundary_SRC = tests/userprog/write-boundary.c \ tests/userprog/boundary.c tests/main.c tests/userprog/write-zero_SRC = tests/userprog/write-zero.c tests/main.c @@ -122,10 +125,12 @@ tests/userprog/close-normal_PUTFILES += tests/userprog/sample.txt tests/userprog/close-twice_PUTFILES += tests/userprog/sample.txt tests/userprog/read-normal_PUTFILES += tests/userprog/sample.txt tests/userprog/read-bad-ptr_PUTFILES += tests/userprog/sample.txt +tests/userprog/read-bad-buf_PUTFILES += tests/userprog/sample.txt tests/userprog/read-boundary_PUTFILES += tests/userprog/sample.txt tests/userprog/read-zero_PUTFILES += tests/userprog/sample.txt tests/userprog/write-normal_PUTFILES += tests/userprog/sample.txt tests/userprog/write-bad-ptr_PUTFILES += tests/userprog/sample.txt +tests/userprog/write-bad-buf_PUTFILES += tests/userprog/sample.txt tests/userprog/write-boundary_PUTFILES += tests/userprog/sample.txt tests/userprog/write-zero_PUTFILES += tests/userprog/sample.txt tests/userprog/multi-child-fd_PUTFILES += tests/userprog/sample.txt diff --git a/src/tests/userprog/Rubric.robustnessCR b/src/tests/userprog/Rubric.robustnessCR index 9784817..256caed 100644 --- a/src/tests/userprog/Rubric.robustnessCR +++ b/src/tests/userprog/Rubric.robustnessCR @@ -1,5 +1,9 @@ -Full robustness of argument passing code: -- Test user stack overflow robustness of "exec" system calls. +Full robustness of argument passing and syscall handling code: +- Test user stack overflow robustness of "exec" system calls and user code. 5 exec-over-arg 5 exec-over-args +5 overflow-stack +- Test syscall user provided buffer validity checks. +5 read-bad-buf +5 write-bad-buf diff --git a/src/tests/userprog/exec-bad-ptr.ck b/src/tests/userprog/exec-bad-ptr.ck index 63f5f78..7f2604c 100644 --- a/src/tests/userprog/exec-bad-ptr.ck +++ b/src/tests/userprog/exec-bad-ptr.ck @@ -2,11 +2,7 @@ use strict; use warnings; use tests::tests; -check_expected ([<<'EOF', <<'EOF']); -(exec-bad-ptr) begin -(exec-bad-ptr) end -exec-bad-ptr: exit(0) -EOF +check_expected ([<<'EOF']); (exec-bad-ptr) begin exec-bad-ptr: exit(-1) EOF diff --git a/src/tests/userprog/open-bad-ptr.ck b/src/tests/userprog/open-bad-ptr.ck index 45349e2..fa686d0 100644 --- a/src/tests/userprog/open-bad-ptr.ck +++ b/src/tests/userprog/open-bad-ptr.ck @@ -2,11 +2,7 @@ use strict; use warnings; use tests::tests; -check_expected ([<<'EOF', <<'EOF']); -(open-bad-ptr) begin -(open-bad-ptr) end -open-bad-ptr: exit(0) -EOF +check_expected ([<<'EOF']); (open-bad-ptr) begin open-bad-ptr: exit(-1) EOF diff --git a/src/tests/userprog/overflow-stack.c b/src/tests/userprog/overflow-stack.c new file mode 100644 index 0000000..d7c9c85 --- /dev/null +++ b/src/tests/userprog/overflow-stack.c @@ -0,0 +1,17 @@ +/* Attempt to overflow the user stack by allocating a 4kB buffer and writing into it. + The process must be terminated with -1 exit code until stack growth has been implemented in Task 3 +*/ + +#include +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + char stack_obj[4096]; + memset (stack_obj, 'a', sizeof stack_obj); + memset (stack_obj+10, '\0', 1); + msg ("buffer: %s", stack_obj); +} diff --git a/src/tests/userprog/overflow-stack.ck b/src/tests/userprog/overflow-stack.ck new file mode 100644 index 0000000..8998230 --- /dev/null +++ b/src/tests/userprog/overflow-stack.ck @@ -0,0 +1,14 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF',<<'EOF']); +(overflow-stack) begin +overflow-stack: exit(-1) +EOF +(overflow-stack) begin +(overflow-stack) buffer: aaaaaaaaaa +(overflow-stack) end +overflow-stack: exit(0) +EOF +pass; diff --git a/src/tests/userprog/read-bad-buf.c b/src/tests/userprog/read-bad-buf.c new file mode 100644 index 0000000..d2dc613 --- /dev/null +++ b/src/tests/userprog/read-bad-buf.c @@ -0,0 +1,17 @@ +/* Passes a buffer to the read system call that starts in valid memory, but runs into kernel space. + The process must be terminated with -1 exit code. +*/ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int handle; + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + + read (handle, (char *) 0xbfffffe0, 100); + fail ("should not have survived read()"); +} diff --git a/src/tests/userprog/read-bad-buf.ck b/src/tests/userprog/read-bad-buf.ck new file mode 100644 index 0000000..703b633 --- /dev/null +++ b/src/tests/userprog/read-bad-buf.ck @@ -0,0 +1,10 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_KERNEL_FAULTS => 1, [<<'EOF']); +(read-bad-buf) begin +(read-bad-buf) open "sample.txt" +read-bad-buf: exit(-1) +EOF +pass; diff --git a/src/tests/userprog/read-bad-ptr.ck b/src/tests/userprog/read-bad-ptr.ck index d10accf..abd67f7 100644 --- a/src/tests/userprog/read-bad-ptr.ck +++ b/src/tests/userprog/read-bad-ptr.ck @@ -2,12 +2,7 @@ use strict; use warnings; use tests::tests; -check_expected ([<<'EOF', <<'EOF']); -(read-bad-ptr) begin -(read-bad-ptr) open "sample.txt" -(read-bad-ptr) end -read-bad-ptr: exit(0) -EOF +check_expected ([<<'EOF']); (read-bad-ptr) begin (read-bad-ptr) open "sample.txt" read-bad-ptr: exit(-1) diff --git a/src/tests/userprog/write-bad-buf.c b/src/tests/userprog/write-bad-buf.c new file mode 100644 index 0000000..589499c --- /dev/null +++ b/src/tests/userprog/write-bad-buf.c @@ -0,0 +1,17 @@ +/* Passes a buffer to the write system call that starts in valid memory, but runs into kernel space. + The process must be terminated with -1 exit code. +*/ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int handle; + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + + write (handle, (char *) 0xbffffff0, 32); + fail ("should have exited with -1"); +} diff --git a/src/tests/userprog/write-bad-buf.ck b/src/tests/userprog/write-bad-buf.ck new file mode 100644 index 0000000..b0678cb --- /dev/null +++ b/src/tests/userprog/write-bad-buf.ck @@ -0,0 +1,10 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_KERNEL_FAULTS => 1, [<<'EOF']); +(write-bad-buf) begin +(write-bad-buf) open "sample.txt" +write-bad-buf: exit(-1) +EOF +pass; diff --git a/src/tests/userprog/write-bad-ptr.ck b/src/tests/userprog/write-bad-ptr.ck index ad9f399..3014879 100644 --- a/src/tests/userprog/write-bad-ptr.ck +++ b/src/tests/userprog/write-bad-ptr.ck @@ -2,12 +2,7 @@ use strict; use warnings; use tests::tests; -check_expected ([<<'EOF', <<'EOF']); -(write-bad-ptr) begin -(write-bad-ptr) open "sample.txt" -(write-bad-ptr) end -write-bad-ptr: exit(0) -EOF +check_expected ([<<'EOF']); (write-bad-ptr) begin (write-bad-ptr) open "sample.txt" write-bad-ptr: exit(-1) From 6e59e8c9f3f183fd13287e510ac5955aa2df790d Mon Sep 17 00:00:00 2001 From: sBubshait Date: Wed, 13 Nov 2024 16:22:16 +0000 Subject: [PATCH 02/11] Update validate_user_pointer to be a void function --- src/userprog/syscall.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/userprog/syscall.c b/src/userprog/syscall.c index b1ce4cc..9d2521a 100644 --- a/src/userprog/syscall.c +++ b/src/userprog/syscall.c @@ -45,7 +45,7 @@ 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); +static void validate_user_pointer (const void *ptr, size_t size); /* A struct defining a syscall_function pointer along with its arity. */ typedef struct @@ -401,7 +401,7 @@ fd_get_file (int fd) fully contained within user virtual memory. Kills the thread (by calling thread_exit) 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 * +static void validate_user_pointer (const void *ptr, size_t size) { if (size > 0 && (ptr == NULL || @@ -409,6 +409,4 @@ validate_user_pointer (const void *ptr, size_t size) !is_user_vaddr (ptr + size - 1) || pagedir_get_page (thread_current()->pagedir, ptr) == NULL)) thread_exit (); - - return (void *) ptr; } From 26de38cdbad5fdc512eaee23afa19067552e3947 Mon Sep 17 00:00:00 2001 From: sBubshait Date: Wed, 13 Nov 2024 16:39:45 +0000 Subject: [PATCH 03/11] Update validate_user_pointer to check if the memory block is mapped into a physical address --- src/userprog/syscall.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/userprog/syscall.c b/src/userprog/syscall.c index 9d2521a..9ddd6e9 100644 --- a/src/userprog/syscall.c +++ b/src/userprog/syscall.c @@ -45,7 +45,7 @@ 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); +static void validate_user_pointer (const void *start, size_t size); /* A struct defining a syscall_function pointer along with its arity. */ typedef struct @@ -397,16 +397,24 @@ fd_get_file (int fd) return hash_entry (e, struct open_file, elem); } -/* Validates if a block of memory starting at PTR and of size SIZE bytes is +/* Validates if a block of memory starting at START and of size SIZE bytes is fully contained within user virtual memory. Kills the thread (by calling - thread_exit) if the memory is invalid. Otherwise, returns the PTR given. - If the size is 0, the function does no checks and returns PTR.*/ + thread_exit) if the memory is invalid. Otherwise, returns (nothing) normally. + If the size is 0, the function does no checks and returns the given ptr. */ static void -validate_user_pointer (const void *ptr, size_t size) +validate_user_pointer (const void *start, size_t size) { - if (size > 0 && (ptr == NULL || - !is_user_vaddr (ptr) || - !is_user_vaddr (ptr + size - 1) || - pagedir_get_page (thread_current()->pagedir, ptr) == NULL)) + if (size == 0) + return; + + const void *end = start + size - 1; + + if (start == NULL || !is_user_vaddr (start) || !is_user_vaddr (end)) thread_exit (); + + /* We now need to check if the entire memory block is mapped to physical + memory by the page table. */ + for (const void *ptr = start; ptr <= end; ptr += PGSIZE) + if (pagedir_get_page (thread_current ()->pagedir, ptr) == NULL) + thread_exit (); } From eb4f23c2903380161bed75f01bb62fb7769ece6f Mon Sep 17 00:00:00 2001 From: sBubshait Date: Wed, 13 Nov 2024 17:11:37 +0000 Subject: [PATCH 04/11] Add validate_user_string helper function to validate that a string is fully conatined within user vm --- src/userprog/syscall.c | 45 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/src/userprog/syscall.c b/src/userprog/syscall.c index 9ddd6e9..d60214d 100644 --- a/src/userprog/syscall.c +++ b/src/userprog/syscall.c @@ -46,6 +46,7 @@ static void syscall_close (int fd); static struct open_file *fd_get_file (int fd); static void validate_user_pointer (const void *start, size_t size); +static void validate_user_string (const char *str); /* A struct defining a syscall_function pointer along with its arity. */ typedef struct @@ -140,7 +141,7 @@ syscall_exit (int status) static pid_t syscall_exec (const char *cmd_line) { - validate_user_pointer (cmd_line, 1); + validate_user_string (cmd_line); pid_t pid = process_execute(cmd_line); @@ -159,9 +160,9 @@ syscall_wait (pid_t pid) pointer. Acquires the file system lock to prevent synchronisation issues, and then uses FILESYS_CREATE to create the file, returning the same status */ static bool -syscall_create (const char *file UNUSED, unsigned initial_size UNUSED) +syscall_create (const char *file, unsigned initial_size) { - validate_user_pointer (file, 1); + validate_user_string (file); lock_acquire (&filesys_lock); bool status = filesys_create (file, initial_size); @@ -176,7 +177,7 @@ syscall_create (const char *file UNUSED, unsigned initial_size UNUSED) static bool syscall_remove (const char *file) { - validate_user_pointer (file, 1); + validate_user_string (file); lock_acquire (&filesys_lock); bool status = filesys_remove (file); @@ -192,7 +193,7 @@ syscall_remove (const char *file) static int syscall_open (const char *file) { - validate_user_pointer (file, 1); + validate_user_string (file); lock_acquire (&filesys_lock); struct file *ptr = filesys_open (file); @@ -418,3 +419,37 @@ validate_user_pointer (const void *start, size_t size) if (pagedir_get_page (thread_current ()->pagedir, ptr) == NULL) thread_exit (); } + +/* Validates if a string is fully contained within user virtual memory. Kills + the thread (by calling thread_exit) if the memory is invalid. Otherwise, + returns (nothing) normally. */ +static void +validate_user_string (const char *str) +{ + if (str == NULL || !is_user_vaddr (str)) + thread_exit (); + + size_t length = 0; + size_t offset = (uintptr_t) str % PGSIZE; + + /* We move page by page, checking if the page is mapped to physical memory. */ + for (;;) + { + void *page = pg_round_down (str + length); + + if (!is_user_vaddr(page) || + pagedir_get_page (thread_current ()->pagedir, page) == NULL) + thread_exit (); + + while (offset < PGSIZE) + { + if (*str == '\0') + return; /* We reached the end of the string without issues. */ + + str++; + offset++; + } + + offset = 0; /* Next page will start at the beginning. */ + } +} From d890c2353ec0d40841241d049ed02c7af255dea9 Mon Sep 17 00:00:00 2001 From: sBubshait Date: Wed, 13 Nov 2024 17:15:56 +0000 Subject: [PATCH 05/11] Add constant MAX_SYSCALL_ARGS to avoid magic numbers [Gleb] --- src/userprog/syscall.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/userprog/syscall.c b/src/userprog/syscall.c index d60214d..0628c02 100644 --- a/src/userprog/syscall.c +++ b/src/userprog/syscall.c @@ -13,6 +13,8 @@ #include #include +#define MAX_SYSCALL_ARGS 3 + static unsigned fd_counter = MIN_USER_FD; struct open_file @@ -96,8 +98,8 @@ static void syscall_handler (struct intr_frame *f) { /* First, read the system call number from the stack. */ - validate_user_pointer (f->esp, 1); - unsigned syscall_number = *(int *) f->esp; + validate_user_pointer (f->esp, sizeof (uintptr_t)); + uintptr_t syscall_number = *(int *) f->esp; /* Ensures the number corresponds to a system call that can be handled. */ if (syscall_number >= LOOKUP_SIZE) @@ -108,9 +110,9 @@ 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)); - uintptr_t args[3] = {0}; - for (int i=0; i < syscall.arity; i++) - args[i] = *(uintptr_t *) (f->esp + sizeof (uintptr_t) * (i + 1)); + 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 there is a return value it is stored in f->eax. */ From 4f586bb4da29a5bd142bf83bdbd4c551faad651d Mon Sep 17 00:00:00 2001 From: sBubshait Date: Wed, 13 Nov 2024 17:42:25 +0000 Subject: [PATCH 06/11] Fix Bug: Free all entries in the fd hashtable when the process exits, w/ E --- src/userprog/process.c | 5 +++++ src/userprog/syscall.c | 14 ++++++++++++++ src/userprog/syscall.h | 1 + 3 files changed, 20 insertions(+) diff --git a/src/userprog/process.c b/src/userprog/process.c index 02adb34..c2076b2 100644 --- a/src/userprog/process.c +++ b/src/userprog/process.c @@ -325,6 +325,11 @@ process_exit (void) uint32_t *pd; printf ("%s: exit(%d)\n", cur->name, cur->exit_status); + + /* Clean up all open files */ + hash_destroy (&cur->open_files, fd_cleanup); + + /* Close the executable file. */ if (cur->exec_file != NULL) { lock_acquire (&filesys_lock); diff --git a/src/userprog/syscall.c b/src/userprog/syscall.c index 0628c02..daf6a8e 100644 --- a/src/userprog/syscall.c +++ b/src/userprog/syscall.c @@ -381,6 +381,20 @@ fd_less (const struct hash_elem *a_, const struct hash_elem *b_, return a->fd < b->fd; } +/* Function to clean up an open file entry. Closes the file and frees the + associated memory. */ +void +fd_cleanup (struct hash_elem *e, void *aux UNUSED) +{ + struct open_file *file_info = hash_entry (e, struct open_file, elem); + + lock_acquire (&filesys_lock); + file_close (file_info->file); + lock_release (&filesys_lock); + + free (file_info); +} + /* Gets a file from its descriptor (FD number). If there is no file with the fd FD it returns NULL. */ static struct open_file * diff --git a/src/userprog/syscall.h b/src/userprog/syscall.h index 16af00c..681a8fb 100644 --- a/src/userprog/syscall.h +++ b/src/userprog/syscall.h @@ -14,5 +14,6 @@ void syscall_init (void); unsigned fd_hash (const struct hash_elem *element, void *aux); bool fd_less (const struct hash_elem *a, const struct hash_elem *b, void *aux); +void fd_cleanup (struct hash_elem *e, void *aux); #endif /* userprog/syscall.h */ From 287130ca7b9186d6c7e43248ef45d39cb9478e4e Mon Sep 17 00:00:00 2001 From: sBubshait Date: Wed, 13 Nov 2024 18:02:08 +0000 Subject: [PATCH 07/11] Update syscall.c to use syscall_exit on failure instead of calling thread_exit directly --- src/userprog/syscall.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/userprog/syscall.c b/src/userprog/syscall.c index daf6a8e..85deffc 100644 --- a/src/userprog/syscall.c +++ b/src/userprog/syscall.c @@ -81,7 +81,6 @@ static const syscall_arguments syscall_lookup[] = static const int LOOKUP_SIZE = sizeof (syscall_lookup) / sizeof (syscall_arguments); - /* Initialises the syscall handling system, as well as a global lock to synchronise all file access between processes. */ void @@ -103,13 +102,14 @@ syscall_handler (struct intr_frame *f) /* Ensures the number corresponds to a system call that can be handled. */ if (syscall_number >= LOOKUP_SIZE) - thread_exit (); + syscall_exit (EXIT_FAILURE); syscall_arguments syscall = syscall_lookup[syscall_number]; /* Next, read and copy the arguments from the stack pointer. */ validate_user_pointer (f->esp + sizeof (uintptr_t), syscall.arity * sizeof (uintptr_t)); + 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)); @@ -427,13 +427,13 @@ validate_user_pointer (const void *start, size_t size) const void *end = start + size - 1; if (start == NULL || !is_user_vaddr (start) || !is_user_vaddr (end)) - thread_exit (); + syscall_exit (EXIT_FAILURE); /* We now need to check if the entire memory block is mapped to physical memory by the page table. */ for (const void *ptr = start; ptr <= end; ptr += PGSIZE) if (pagedir_get_page (thread_current ()->pagedir, ptr) == NULL) - thread_exit (); + syscall_exit (EXIT_FAILURE); } /* Validates if a string is fully contained within user virtual memory. Kills @@ -443,7 +443,7 @@ static void validate_user_string (const char *str) { if (str == NULL || !is_user_vaddr (str)) - thread_exit (); + syscall_exit (EXIT_FAILURE); size_t length = 0; size_t offset = (uintptr_t) str % PGSIZE; @@ -455,7 +455,7 @@ validate_user_string (const char *str) if (!is_user_vaddr(page) || pagedir_get_page (thread_current ()->pagedir, page) == NULL) - thread_exit (); + syscall_exit (EXIT_FAILURE); while (offset < PGSIZE) { From b1c58194695b8321f532e9f715254c91f9cb6f6e Mon Sep 17 00:00:00 2001 From: sBubshait Date: Wed, 13 Nov 2024 18:06:51 +0000 Subject: [PATCH 08/11] Avoid masking the struct syscall_arguments using typedef for consistency --- src/userprog/syscall.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/userprog/syscall.c b/src/userprog/syscall.c index 85deffc..34f85fe 100644 --- a/src/userprog/syscall.c +++ b/src/userprog/syscall.c @@ -14,6 +14,7 @@ #include #define MAX_SYSCALL_ARGS 3 +#define EXIT_FAILURE -1 static unsigned fd_counter = MIN_USER_FD; @@ -51,15 +52,15 @@ static void validate_user_pointer (const void *start, size_t size); static void validate_user_string (const char *str); /* A struct defining a syscall_function pointer along with its arity. */ -typedef struct +struct syscall_arguments { syscall_function function; /* Function pointer. */ int arity; /* Number of arguments of the function. */ - } syscall_arguments; + }; /* A look-up table mapping numbers to system call functions with their number of arguments. */ -static const syscall_arguments syscall_lookup[] = +static const struct syscall_arguments syscall_lookup[] = { [SYS_HALT] = {(syscall_function) syscall_halt, 0}, [SYS_EXIT] = {(syscall_function) syscall_exit, 1}, @@ -79,7 +80,7 @@ static const syscall_arguments syscall_lookup[] = /* The number of syscall functions (i.e, number of elements) within the syscall_lookup table. */ static const int LOOKUP_SIZE - = sizeof (syscall_lookup) / sizeof (syscall_arguments); + = sizeof (syscall_lookup) / sizeof (struct syscall_arguments); /* Initialises the syscall handling system, as well as a global lock to synchronise all file access between processes. */ @@ -104,7 +105,7 @@ syscall_handler (struct intr_frame *f) if (syscall_number >= LOOKUP_SIZE) syscall_exit (EXIT_FAILURE); - syscall_arguments syscall = syscall_lookup[syscall_number]; + struct syscall_arguments syscall = syscall_lookup[syscall_number]; /* Next, read and copy the arguments from the stack pointer. */ validate_user_pointer (f->esp + sizeof (uintptr_t), From 9549ca28e5bacc08362047f8e79eea43b582f763 Mon Sep 17 00:00:00 2001 From: sBubshait Date: Wed, 13 Nov 2024 18:18:41 +0000 Subject: [PATCH 09/11] Refactor syscall: Use EXIT_FAILURE instead of magic numbers & close files on failure --- src/userprog/syscall.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/userprog/syscall.c b/src/userprog/syscall.c index 34f85fe..0c352f5 100644 --- a/src/userprog/syscall.c +++ b/src/userprog/syscall.c @@ -138,7 +138,6 @@ syscall_exit (int status) } /* Executes a given command with the relevant args, by calling process_execute. - Acquires the filesystem lock as process_execute accesses the file system. Returns PID for the process that is running the CMD_LINE */ static pid_t @@ -146,9 +145,7 @@ syscall_exec (const char *cmd_line) { validate_user_string (cmd_line); - pid_t pid = process_execute(cmd_line); - - return pid; + return process_execute (cmd_line); /* Returns the PID of the new process */ } /* Handles the syscall of wait. Effectively a wrapper for process_wait as the @@ -156,7 +153,7 @@ syscall_exec (const char *cmd_line) static int syscall_wait (pid_t pid) { - return process_wait (pid); + return process_wait (pid); /* Returns the exit status of the waited process */ } /* Handles the syscall for file creation. First validates the user file @@ -202,14 +199,17 @@ syscall_open (const char *file) struct file *ptr = filesys_open (file); lock_release (&filesys_lock); if (ptr == NULL) - return -1; + return EXIT_FAILURE; /* Allocate space for a struct representing a mapping from an FD to a struct file. */ struct open_file *file_info = (struct open_file*) malloc (sizeof (struct open_file)); if (file_info == NULL) - return -1; + { + file_close (ptr); + return EXIT_FAILURE; + } /* Populate the above struct, with a unique FD and the current open file */ file_info->fd = fd_counter++; @@ -230,7 +230,7 @@ syscall_filesize (int fd) { struct open_file *file_info = fd_get_file (fd); if (file_info == NULL) - return -1; + return EXIT_FAILURE; lock_acquire (&filesys_lock); int bytes = file_length (file_info->file); @@ -249,7 +249,7 @@ syscall_read (int fd, void *buffer, unsigned size) /* Only console (fd = 0) or other files, not including STDOUT, (fd > 1) are allowed. */ if (fd < 0 || fd == STDOUT_FILENO) - return -1; + return EXIT_FAILURE; validate_user_pointer (buffer, size); @@ -267,7 +267,7 @@ syscall_read (int fd, void *buffer, unsigned size) /* Reading from a file. */ struct open_file *file_info = fd_get_file (fd); if (file_info == NULL) - return -1; + return EXIT_FAILURE; lock_acquire (&filesys_lock); int bytes_written = file_read (file_info->file, buffer, size); From 31ea2158052d99bec16aea64bf650ab38cf18a84 Mon Sep 17 00:00:00 2001 From: sBubshait Date: Wed, 13 Nov 2024 18:30:24 +0000 Subject: [PATCH 10/11] Refactor validate_user_string to remove unnecessary variable to track length of str --- src/userprog/syscall.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/userprog/syscall.c b/src/userprog/syscall.c index 0c352f5..a85fc2b 100644 --- a/src/userprog/syscall.c +++ b/src/userprog/syscall.c @@ -446,13 +446,12 @@ validate_user_string (const char *str) if (str == NULL || !is_user_vaddr (str)) syscall_exit (EXIT_FAILURE); - size_t length = 0; size_t offset = (uintptr_t) str % PGSIZE; /* We move page by page, checking if the page is mapped to physical memory. */ for (;;) { - void *page = pg_round_down (str + length); + void *page = pg_round_down (str); if (!is_user_vaddr(page) || pagedir_get_page (thread_current ()->pagedir, page) == NULL) From fa2fb4a711cbe6d63b4944481b57856fde9608bd Mon Sep 17 00:00:00 2001 From: sBubshait Date: Wed, 13 Nov 2024 18:48:23 +0000 Subject: [PATCH 11/11] Refactor system call comments for accuracy and grammar --- src/userprog/syscall.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/userprog/syscall.c b/src/userprog/syscall.c index a85fc2b..29231f3 100644 --- a/src/userprog/syscall.c +++ b/src/userprog/syscall.c @@ -91,7 +91,7 @@ syscall_init (void) lock_init (&filesys_lock); } -/* Function that takes a interrupt frame containing a syscall and its args. +/* Function that takes an interrupt frame containing a syscall and its args. Validates the arguments and pointers before calling the relevant high-level system call function, storing its output (if any) in f->eax */ static void @@ -138,8 +138,7 @@ syscall_exit (int status) } /* Executes a given command with the relevant args, by calling process_execute. - Returns PID for the process that is running the CMD_LINE - */ + Returns PID for the process that is running the CMD_LINE. */ static pid_t syscall_exec (const char *cmd_line) { @@ -416,8 +415,8 @@ fd_get_file (int fd) } /* Validates if a block of memory starting at START and of size SIZE bytes is - fully contained within user virtual memory. Kills the thread (by calling - thread_exit) if the memory is invalid. Otherwise, returns (nothing) normally. + fully contained within user virtual memory. Kills the thread (by exiting with + failure) if the memory is invalid. Otherwise, returns (nothing) normally. If the size is 0, the function does no checks and returns the given ptr. */ static void validate_user_pointer (const void *start, size_t size) @@ -438,7 +437,7 @@ validate_user_pointer (const void *start, size_t size) } /* Validates if a string is fully contained within user virtual memory. Kills - the thread (by calling thread_exit) if the memory is invalid. Otherwise, + the thread (by exiting with failure) if the memory is invalid. Otherwise, returns (nothing) normally. */ static void validate_user_string (const char *str)