diff --git a/src/userprog/process.c b/src/userprog/process.c index 61f337d..20e2677 100644 --- a/src/userprog/process.c +++ b/src/userprog/process.c @@ -16,9 +16,18 @@ #include "threads/interrupt.h" #include "threads/synch.h" #include "threads/palloc.h" +#include "threads/malloc.h" #include "threads/thread.h" #include "threads/vaddr.h" -#include "devices/timer.h" +#include "threads/synch.h" + +/* Keeps track of the position of pointers to user program arguments + within a linked list. */ +struct arg_elem + { + char* arg; + struct list_elem elem; + }; static thread_func start_process NO_RETURN; static bool load (const char *cmdline, void (**eip) (void), void **esp); @@ -38,6 +47,9 @@ process_execute (const char *file_name) fn_copy = palloc_get_page (0); if (fn_copy == NULL) return TID_ERROR; + + /* Imposing implicit limit that the command line arguments + including the user program name fit within a single page. */ strlcpy (fn_copy, file_name, PGSIZE); /* Create a new thread to execute FILE_NAME. */ @@ -47,15 +59,30 @@ process_execute (const char *file_name) return tid; } +static void * +push_to_stack (void **esp, void *data, size_t data_size) +{ + *esp -= data_size; + memcpy (*esp, data, data_size); + return *esp; +} + /* A thread function that loads a user process and starts it running. */ static void start_process (void *file_name_) { - char *file_name = file_name_; struct intr_frame if_; bool success; + /* Retrieve first argument of command, which is the file name + of the process. */ + char *saveptr; + char *arg = strtok_r (file_name_, " ", &saveptr); + + char file_name[15]; + strlcpy (file_name, arg, 15); + /* Initialize interrupt frame and load executable. */ memset (&if_, 0, sizeof if_); if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG; @@ -63,8 +90,93 @@ start_process (void *file_name_) if_.eflags = FLAG_IF | FLAG_MBS; success = load (file_name, &if_.eip, &if_.esp); + /* Load command line argument *data* to user process stack. + This can't cause overflow due to enforcing that the size of + command line input must fit in a page. Also keep track + of pointers to the argument data within a linked list. */ + char *file_name_ptr = push_to_stack (&if_.esp, file_name, + (strlen (file_name) + 1) + * sizeof (char)); + + struct list arg_list; + list_init (&arg_list); + + int arg_count = 1; + while (arg != NULL) + { + push_to_stack (&if_.esp, arg, (strlen (arg) + 1) * sizeof (char)); + + struct arg_elem *arg_elem = malloc (sizeof (struct arg_elem)); + if (arg_elem == NULL) + { + printf("ERROR: Couldn't allocate argument pointer memory for %s!\n", + file_name); + palloc_free_page (file_name_); + if (success) process_exit (); + thread_exit (); + } + + arg_elem->arg = arg; + list_push_front (&arg_list, &arg_elem->elem); + + arg_count++; + arg = strtok_r (NULL, " ", &saveptr); + } + + /* Calculate the remaining number of bytes that need to be written + to the user process stack in order to check for possible overflow. */ + size_t align_size = ((unsigned int) if_.esp % 4) * sizeof (uint8_t); + size_t argv_data_size = (arg_count + 1) * sizeof (char *); + size_t argv_size = sizeof (char **); + size_t argc_size = sizeof (int); + size_t return_addr_size = sizeof (void *); + size_t remaining_size = align_size + argv_data_size + argv_size + argc_size + + return_addr_size; + + /* If pushing the rest of the data required for the stack would cause + overflow, allocate an extra page. */ + if (PHYS_BASE - if_.esp + remaining_size > PGSIZE) + { + /* TODO: Allocate an extra page for the rest of the process stack. */ + } + + /* Align stack pointer to word size before pushing argv elements for + performance. */ + if_.esp -= align_size; + + /* Push a null pointer sentinel inside argv. */ + if_.esp -= sizeof (char *); + *(char *) if_.esp = NULL; + + /* Push pointers to process arguments from argument linked list */ + struct list_elem *e = list_begin (&arg_list); + struct list_elem *tail = list_tail (&arg_list); + while (e != tail) + { + struct arg_elem *arg_elem = list_entry (e, struct arg_elem, elem); + + push_to_stack (&if_.esp, &arg_elem->arg, sizeof (arg_elem->arg)); + + e = list_next (e); + free (arg_elem); + } + + /* Push pointer to the process file name to the stack. */ + char **argv = push_to_stack (&if_.esp, &file_name_ptr, + sizeof (file_name_ptr)); + + /* Push pointer to the start of argv array. */ + push_to_stack (&if_.esp, &argv, sizeof(argv)); + + /* Push the number of arguments to the stack. */ + push_to_stack (&if_.esp, &arg_count, sizeof (arg_count)); + + /* Push fake return address (null pointer). */ + if_.esp -= sizeof (char *); + *(char *) if_.esp = NULL; + /* If load failed, quit. */ - palloc_free_page (file_name); + palloc_free_page (file_name_); if (!success) thread_exit ();