Compare commits

..

5 Commits

18 changed files with 152 additions and 238 deletions

View File

@@ -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-empty create-null create-bad-ptr create-long create-exists \
create-bound open-normal open-missing open-boundary open-empty \ create-bound open-normal open-missing open-boundary open-empty \
open-null open-bad-ptr open-twice close-normal close-twice close-stdin \ open-null open-bad-ptr open-twice close-normal close-twice close-stdin \
close-stdout close-bad-fd read-normal read-bad-ptr read-bad-buf read-boundary \ close-stdout close-bad-fd read-normal read-bad-ptr read-boundary \
read-zero read-stdout read-bad-fd write-normal write-bad-ptr write-bad-buf \ read-zero read-stdout read-bad-fd write-normal write-bad-ptr \
write-boundary write-zero write-stdin write-bad-fd exec-once exec-arg \ 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-large-arg exec-multiple exec-missing exec-over-arg exec-over-args \
exec-bad-ptr wait-simple wait-twice wait-killed wait-load-kill \ 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 \ 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 \ rox-child rox-multichild bad-read bad-write bad-read2 bad-write2 \
bad-jump bad-jump2 bad-maths overflow-stack) bad-jump bad-jump2 bad-maths)
tests/userprog_PROGS = $(tests/userprog_TESTS) $(addprefix \ tests/userprog_PROGS = $(tests/userprog_TESTS) $(addprefix \
tests/userprog/,child-simple child-args child-bad child-close child-rox exec-exit) tests/userprog/,child-simple child-args child-bad child-close child-rox exec-exit)
@@ -36,7 +36,6 @@ 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-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-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/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/sc-boundary_SRC = tests/userprog/sc-boundary.c \
tests/userprog/boundary.c tests/main.c tests/userprog/boundary.c tests/main.c
tests/userprog/sc-boundary-2_SRC = tests/userprog/sc-boundary-2.c \ tests/userprog/sc-boundary-2_SRC = tests/userprog/sc-boundary-2.c \
@@ -67,7 +66,6 @@ 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/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-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-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/read-boundary_SRC = tests/userprog/read-boundary.c \
tests/userprog/boundary.c tests/main.c tests/userprog/boundary.c tests/main.c
tests/userprog/read-zero_SRC = tests/userprog/read-zero.c tests/main.c tests/userprog/read-zero_SRC = tests/userprog/read-zero.c tests/main.c
@@ -75,7 +73,6 @@ 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/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-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-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/write-boundary_SRC = tests/userprog/write-boundary.c \
tests/userprog/boundary.c tests/main.c tests/userprog/boundary.c tests/main.c
tests/userprog/write-zero_SRC = tests/userprog/write-zero.c tests/main.c tests/userprog/write-zero_SRC = tests/userprog/write-zero.c tests/main.c
@@ -125,12 +122,10 @@ tests/userprog/close-normal_PUTFILES += tests/userprog/sample.txt
tests/userprog/close-twice_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-normal_PUTFILES += tests/userprog/sample.txt
tests/userprog/read-bad-ptr_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-boundary_PUTFILES += tests/userprog/sample.txt
tests/userprog/read-zero_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-normal_PUTFILES += tests/userprog/sample.txt
tests/userprog/write-bad-ptr_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-boundary_PUTFILES += tests/userprog/sample.txt
tests/userprog/write-zero_PUTFILES += tests/userprog/sample.txt tests/userprog/write-zero_PUTFILES += tests/userprog/sample.txt
tests/userprog/multi-child-fd_PUTFILES += tests/userprog/sample.txt tests/userprog/multi-child-fd_PUTFILES += tests/userprog/sample.txt

View File

@@ -1,9 +1,5 @@
Full robustness of argument passing and syscall handling code: Full robustness of argument passing code:
- Test user stack overflow robustness of "exec" system calls and user code. - Test user stack overflow robustness of "exec" system calls.
5 exec-over-arg 5 exec-over-arg
5 exec-over-args 5 exec-over-args
5 overflow-stack
- Test syscall user provided buffer validity checks.
5 read-bad-buf
5 write-bad-buf

View File

@@ -2,7 +2,11 @@
use strict; use strict;
use warnings; use warnings;
use tests::tests; use tests::tests;
check_expected ([<<'EOF']); check_expected ([<<'EOF', <<'EOF']);
(exec-bad-ptr) begin
(exec-bad-ptr) end
exec-bad-ptr: exit(0)
EOF
(exec-bad-ptr) begin (exec-bad-ptr) begin
exec-bad-ptr: exit(-1) exec-bad-ptr: exit(-1)
EOF EOF

View File

@@ -2,7 +2,11 @@
use strict; use strict;
use warnings; use warnings;
use tests::tests; use tests::tests;
check_expected ([<<'EOF']); check_expected ([<<'EOF', <<'EOF']);
(open-bad-ptr) begin
(open-bad-ptr) end
open-bad-ptr: exit(0)
EOF
(open-bad-ptr) begin (open-bad-ptr) begin
open-bad-ptr: exit(-1) open-bad-ptr: exit(-1)
EOF EOF

View File

@@ -1,17 +0,0 @@
/* 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 <string.h>
#include <syscall.h>
#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);
}

View File

@@ -1,14 +0,0 @@
# -*- 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;

View File

@@ -1,17 +0,0 @@
/* 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 <syscall.h>
#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()");
}

View File

@@ -1,10 +0,0 @@
# -*- 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;

View File

@@ -2,7 +2,12 @@
use strict; use strict;
use warnings; use warnings;
use tests::tests; use tests::tests;
check_expected ([<<'EOF']); check_expected ([<<'EOF', <<'EOF']);
(read-bad-ptr) begin
(read-bad-ptr) open "sample.txt"
(read-bad-ptr) end
read-bad-ptr: exit(0)
EOF
(read-bad-ptr) begin (read-bad-ptr) begin
(read-bad-ptr) open "sample.txt" (read-bad-ptr) open "sample.txt"
read-bad-ptr: exit(-1) read-bad-ptr: exit(-1)

View File

@@ -1,17 +0,0 @@
/* 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 <syscall.h>
#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");
}

View File

@@ -1,10 +0,0 @@
# -*- 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;

View File

@@ -2,7 +2,12 @@
use strict; use strict;
use warnings; use warnings;
use tests::tests; use tests::tests;
check_expected ([<<'EOF']); check_expected ([<<'EOF', <<'EOF']);
(write-bad-ptr) begin
(write-bad-ptr) open "sample.txt"
(write-bad-ptr) end
write-bad-ptr: exit(0)
EOF
(write-bad-ptr) begin (write-bad-ptr) begin
(write-bad-ptr) open "sample.txt" (write-bad-ptr) open "sample.txt"
write-bad-ptr: exit(-1) write-bad-ptr: exit(-1)

View File

@@ -212,7 +212,6 @@ donate_priority (struct thread *donee) {
ASSERT (intr_get_level () == INTR_OFF); ASSERT (intr_get_level () == INTR_OFF);
struct thread *donor = thread_current (); struct thread *donor = thread_current ();
list_remove (&donor->donor_elem);
list_push_back (&donee->donors_list, &donor->donor_elem); list_push_back (&donee->donors_list, &donor->donor_elem);
while (donee != NULL) while (donee != NULL)
@@ -261,7 +260,6 @@ lock_acquire (struct lock *lock)
ASSERT (!lock_held_by_current_thread (lock)); ASSERT (!lock_held_by_current_thread (lock));
struct thread *t = thread_current (); struct thread *t = thread_current ();
ASSERT (t->waiting_lock == NULL);
enum intr_level old_level = intr_disable (); enum intr_level old_level = intr_disable ();
if (lock->holder != NULL) if (lock->holder != NULL)
@@ -343,6 +341,7 @@ lock_release (struct lock *lock)
released, transfer the remaining orphaned donors to its donor list. */ released, transfer the remaining orphaned donors to its donor list. */
if (max_donor != NULL) if (max_donor != NULL)
{ {
list_remove (&max_donor->donor_elem);
while (!list_empty (&orphan_list)) while (!list_empty (&orphan_list))
list_push_back (&max_donor->donors_list, list_pop_front (&orphan_list)); list_push_back (&max_donor->donors_list, list_pop_front (&orphan_list));
} }

View File

@@ -373,9 +373,7 @@ thread_exit (void)
and schedule another process. That process will destroy us and schedule another process. That process will destroy us
when it calls thread_schedule_tail(). */ when it calls thread_schedule_tail(). */
intr_disable (); intr_disable ();
struct thread *t = thread_current (); list_remove (&thread_current()->allelem);
list_remove (&t->allelem);
list_remove (&t->donor_elem);
thread_current ()->status = THREAD_DYING; thread_current ()->status = THREAD_DYING;
schedule (); schedule ();
NOT_REACHED (); NOT_REACHED ();
@@ -681,7 +679,6 @@ init_thread (struct thread *t, const char *name, int nice, int priority,
t->base_priority t->base_priority
= thread_mlfqs ? calculate_bsd_priority (recent_cpu, nice) : priority; = thread_mlfqs ? calculate_bsd_priority (recent_cpu, nice) : priority;
list_init (&t->donors_list); list_init (&t->donors_list);
list_push_back (&t->donors_list, &t->donor_elem);
t->waiting_lock = NULL; t->waiting_lock = NULL;
t->nice = nice; t->nice = nice;

View File

@@ -145,6 +145,14 @@ page_fault (struct intr_frame *f)
write = (f->error_code & PF_W) != 0; write = (f->error_code & PF_W) != 0;
user = (f->error_code & PF_U) != 0; user = (f->error_code & PF_U) != 0;
/* Kernel page fault is further handled by the kernel itself. */
if (!user)
{
f->eip = (void *)f->eax;
f->eax = 0xffffffff;
return;
}
/* To implement virtual memory, delete the rest of the function /* To implement virtual memory, delete the rest of the function
body, and replace it with code that brings in the page to body, and replace it with code that brings in the page to
which fault_addr refers. */ which fault_addr refers. */

View File

@@ -8,7 +8,6 @@
#include <string.h> #include <string.h>
#include "userprog/gdt.h" #include "userprog/gdt.h"
#include "userprog/pagedir.h" #include "userprog/pagedir.h"
#include "userprog/syscall.h"
#include "userprog/tss.h" #include "userprog/tss.h"
#include "filesys/directory.h" #include "filesys/directory.h"
#include "filesys/file.h" #include "filesys/file.h"
@@ -82,14 +81,8 @@ process_execute (const char *cmd)
of the process. */ of the process. */
char *file_name = strtok_r (cmd_copy, " ", &data->cmd_saveptr); char *file_name = strtok_r (cmd_copy, " ", &data->cmd_saveptr);
/* NOTE: Currently, the file being executed is closed in load () and then /* Validates that the current file to be executed is a valid file */
reopened here. Because load is an exported public function, this if (filesys_open (file_name) == NULL)
might be necessary. */
lock_acquire (&filesys_lock);
/* Validates that the current file to be executed is a valid file */
bool valid_file = filesys_open (file_name) != NULL;
lock_release (&filesys_lock);
if (!valid_file)
return TID_ERROR; return TID_ERROR;
/* Create a new thread to execute the command, by initializing /* Create a new thread to execute the command, by initializing
@@ -127,15 +120,6 @@ start_process (void *proc_start_data)
if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG; if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG;
if_.cs = SEL_UCSEG; if_.cs = SEL_UCSEG;
if_.eflags = FLAG_IF | FLAG_MBS; if_.eflags = FLAG_IF | FLAG_MBS;
lock_acquire (&filesys_lock);
/* Prevent writing to the file being executed. */
struct file *exec_file = filesys_open (data->file_name);
thread_current ()->exec_file = exec_file;
file_deny_write (exec_file);
lock_release (&filesys_lock);
success = load (data->file_name, &if_.eip, &if_.esp); success = load (data->file_name, &if_.eip, &if_.esp);
/* If load failed, quit. */ /* If load failed, quit. */
@@ -157,6 +141,13 @@ start_process (void *proc_start_data)
goto fail; goto fail;
} }
/* NOTE: Currently, the file being executed is closed in load () and then
reopened here. Because load is an exported public function, this
might be necessary. */
struct file *exec_file = filesys_open (data->file_name);
thread_current ()->exec_file = exec_file;
file_deny_write (exec_file);
/* Start the user process by simulating a return from an /* Start the user process by simulating a return from an
interrupt, implemented by intr_exit (in interrupt, implemented by intr_exit (in
threads/intr-stubs.S). Because intr_exit takes all of its threads/intr-stubs.S). Because intr_exit takes all of its
@@ -325,18 +316,7 @@ process_exit (void)
uint32_t *pd; uint32_t *pd;
printf ("%s: exit(%d)\n", cur->name, cur->exit_status); printf ("%s: exit(%d)\n", cur->name, cur->exit_status);
file_close (cur->exec_file);
/* 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);
file_allow_write (cur->exec_file);
file_close (cur->exec_file);
lock_release (&filesys_lock);
}
/* Update process result. */ /* Update process result. */
if (cur->result != NULL) if (cur->result != NULL)
@@ -496,7 +476,6 @@ load (const char *file_name, void (**eip) (void), void **esp)
off_t file_ofs; off_t file_ofs;
bool success = false; bool success = false;
int i; int i;
lock_acquire (&filesys_lock);
/* Allocate and activate page directory. */ /* Allocate and activate page directory. */
t->pagedir = pagedir_create (); t->pagedir = pagedir_create ();
@@ -596,7 +575,6 @@ load (const char *file_name, void (**eip) (void), void **esp)
done: done:
/* We arrive here whether the load is successful or not. */ /* We arrive here whether the load is successful or not. */
file_close (file); file_close (file);
lock_release (&filesys_lock);
return success; return success;
} }

View File

@@ -11,11 +11,12 @@
#include "userprog/process.h" #include "userprog/process.h"
#include "userprog/pagedir.h" #include "userprog/pagedir.h"
#include <stdio.h> #include <stdio.h>
#include <stdbool.h>
#include <syscall-nr.h> #include <syscall-nr.h>
#define MAX_SYSCALL_ARGS 3 #define MAX_SYSCALL_ARGS 3
#define EXIT_FAILURE -1
static struct lock filesys_lock;
static unsigned fd_counter = MIN_USER_FD; static unsigned fd_counter = MIN_USER_FD;
struct open_file struct open_file
@@ -48,19 +49,22 @@ static unsigned syscall_tell (int fd);
static void syscall_close (int fd); static void syscall_close (int fd);
static struct open_file *fd_get_file (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_pointer (const void *ptr, size_t size,
static void validate_user_string (const char *str); 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);
/* A struct defining a syscall_function pointer along with its arity. */ /* A struct defining a syscall_function pointer along with its arity. */
struct syscall_arguments typedef struct
{ {
syscall_function function; /* Function pointer. */ syscall_function function; /* Function pointer. */
int arity; /* Number of arguments of the function. */ int arity; /* Number of arguments of the function. */
}; } syscall_arguments;
/* A look-up table mapping numbers to system call functions with their number of /* A look-up table mapping numbers to system call functions with their number of
arguments. */ arguments. */
static const struct syscall_arguments syscall_lookup[] = static const syscall_arguments syscall_lookup[] =
{ {
[SYS_HALT] = {(syscall_function) syscall_halt, 0}, [SYS_HALT] = {(syscall_function) syscall_halt, 0},
[SYS_EXIT] = {(syscall_function) syscall_exit, 1}, [SYS_EXIT] = {(syscall_function) syscall_exit, 1},
@@ -80,7 +84,8 @@ static const struct syscall_arguments syscall_lookup[] =
/* The number of syscall functions (i.e, number of elements) within the /* The number of syscall functions (i.e, number of elements) within the
syscall_lookup table. */ syscall_lookup table. */
static const int LOOKUP_SIZE static const int LOOKUP_SIZE
= sizeof (syscall_lookup) / sizeof (struct syscall_arguments); = sizeof (syscall_lookup) / sizeof (syscall_arguments);
/* Initialises the syscall handling system, as well as a global lock to /* Initialises the syscall handling system, as well as a global lock to
synchronise all file access between processes. */ synchronise all file access between processes. */
@@ -91,29 +96,28 @@ syscall_init (void)
lock_init (&filesys_lock); lock_init (&filesys_lock);
} }
/* Function that takes an interrupt frame containing a syscall and its args. /* Function that takes a interrupt frame containing a syscall and its args.
Validates the arguments and pointers before calling the relevant Validates the arguments and pointers before calling the relevant
high-level system call function, storing its output (if any) in f->eax */ high-level system call function, storing its output (if any) in f->eax */
static void static void
syscall_handler (struct intr_frame *f) syscall_handler (struct intr_frame *f)
{ {
/* First, read the system call number from the stack. */ /* First, read the system call number from the stack. */
validate_user_pointer (f->esp, sizeof (uintptr_t)); validate_user_pointer (f->esp, sizeof (uintptr_t), false);
uintptr_t syscall_number = *(int *) f->esp; uintptr_t syscall_number = *(int *)f->esp;
/* Ensures the number corresponds to a system call that can be handled. */ /* Ensures the number corresponds to a system call that can be handled. */
if (syscall_number >= LOOKUP_SIZE) if (syscall_number >= LOOKUP_SIZE)
syscall_exit (EXIT_FAILURE); thread_exit ();
struct syscall_arguments syscall = syscall_lookup[syscall_number]; syscall_arguments syscall = syscall_lookup[syscall_number];
/* Next, read and copy the arguments from the stack pointer. */ /* Next, read and copy the arguments from the stack pointer. */
validate_user_pointer (f->esp + sizeof (uintptr_t), validate_user_pointer (f->esp + sizeof (uintptr_t),
syscall.arity * sizeof (uintptr_t)); syscall.arity * sizeof (uintptr_t), false);
uintptr_t args[MAX_SYSCALL_ARGS] = { 0 };
uintptr_t args[MAX_SYSCALL_ARGS] = {0};
for (int i = 0; i < syscall.arity && i < MAX_SYSCALL_ARGS; i++) for (int i = 0; i < syscall.arity && i < MAX_SYSCALL_ARGS; i++)
args[i] = *(uintptr_t *) (f->esp + sizeof (uintptr_t) * (i + 1)); args[i] = *(uintptr_t *)(f->esp + sizeof (uintptr_t) * (i + 1));
/* Call the function that handles this system call with the arguments. When /* Call the function that handles this system call with the arguments. When
there is a return value it is stored in f->eax. */ there is a return value it is stored in f->eax. */
@@ -138,13 +142,19 @@ syscall_exit (int status)
} }
/* Executes a given command with the relevant args, by calling process_execute. /* Executes a given command with the relevant args, by calling process_execute.
Returns PID for the process that is running the CMD_LINE. */ 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 static pid_t
syscall_exec (const char *cmd_line) syscall_exec (const char *cmd_line)
{ {
validate_user_string (cmd_line); validate_user_string (cmd_line, false);
return process_execute (cmd_line); /* Returns the PID of the new process */ lock_acquire (&filesys_lock);
pid_t pid = process_execute(cmd_line);
lock_release (&filesys_lock);
return pid;
} }
/* Handles the syscall of wait. Effectively a wrapper for process_wait as the /* Handles the syscall of wait. Effectively a wrapper for process_wait as the
@@ -152,16 +162,16 @@ syscall_exec (const char *cmd_line)
static int static int
syscall_wait (pid_t pid) syscall_wait (pid_t pid)
{ {
return process_wait (pid); /* Returns the exit status of the waited process */ return process_wait (pid);
} }
/* Handles the syscall for file creation. First validates the user file /* Handles the syscall for file creation. First validates the user file
pointer. Acquires the file system lock to prevent synchronisation issues, pointer. Acquires the file system lock to prevent synchronisation issues,
and then uses FILESYS_CREATE to create the file, returning the same status */ and then uses FILESYS_CREATE to create the file, returning the same status */
static bool static bool
syscall_create (const char *file, unsigned initial_size) syscall_create (const char *file UNUSED, unsigned initial_size UNUSED)
{ {
validate_user_string (file); validate_user_string (file, false);
lock_acquire (&filesys_lock); lock_acquire (&filesys_lock);
bool status = filesys_create (file, initial_size); bool status = filesys_create (file, initial_size);
@@ -176,7 +186,7 @@ syscall_create (const char *file, unsigned initial_size)
static bool static bool
syscall_remove (const char *file) syscall_remove (const char *file)
{ {
validate_user_string (file); validate_user_string (file, false);
lock_acquire (&filesys_lock); lock_acquire (&filesys_lock);
bool status = filesys_remove (file); bool status = filesys_remove (file);
@@ -192,23 +202,20 @@ syscall_remove (const char *file)
static int static int
syscall_open (const char *file) syscall_open (const char *file)
{ {
validate_user_string (file); validate_user_string (file, false);
lock_acquire (&filesys_lock); lock_acquire (&filesys_lock);
struct file *ptr = filesys_open (file); struct file *ptr = filesys_open (file);
lock_release (&filesys_lock); lock_release (&filesys_lock);
if (ptr == NULL) if (ptr == NULL)
return EXIT_FAILURE; return -1;
/* Allocate space for a struct representing a mapping from an FD to a struct /* Allocate space for a struct representing a mapping from an FD to a struct
file. */ file. */
struct open_file *file_info struct open_file *file_info
= (struct open_file*) malloc (sizeof (struct open_file)); = (struct open_file*) malloc (sizeof (struct open_file));
if (file_info == NULL) 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 */ /* Populate the above struct, with a unique FD and the current open file */
file_info->fd = fd_counter++; file_info->fd = fd_counter++;
@@ -229,7 +236,7 @@ syscall_filesize (int fd)
{ {
struct open_file *file_info = fd_get_file (fd); struct open_file *file_info = fd_get_file (fd);
if (file_info == NULL) if (file_info == NULL)
return EXIT_FAILURE; return -1;
lock_acquire (&filesys_lock); lock_acquire (&filesys_lock);
int bytes = file_length (file_info->file); int bytes = file_length (file_info->file);
@@ -248,9 +255,9 @@ syscall_read (int fd, void *buffer, unsigned size)
/* Only console (fd = 0) or other files, not including STDOUT, (fd > 1) are /* Only console (fd = 0) or other files, not including STDOUT, (fd > 1) are
allowed. */ allowed. */
if (fd < 0 || fd == STDOUT_FILENO) if (fd < 0 || fd == STDOUT_FILENO)
return EXIT_FAILURE; return -1;
validate_user_pointer (buffer, size); validate_user_pointer (buffer, size, true);
if (fd == STDIN_FILENO) if (fd == STDIN_FILENO)
{ {
@@ -266,7 +273,7 @@ syscall_read (int fd, void *buffer, unsigned size)
/* Reading from a file. */ /* Reading from a file. */
struct open_file *file_info = fd_get_file (fd); struct open_file *file_info = fd_get_file (fd);
if (file_info == NULL) if (file_info == NULL)
return EXIT_FAILURE; return -1;
lock_acquire (&filesys_lock); lock_acquire (&filesys_lock);
int bytes_written = file_read (file_info->file, buffer, size); int bytes_written = file_read (file_info->file, buffer, size);
@@ -287,7 +294,7 @@ syscall_write (int fd, const void *buffer, unsigned size)
if (fd <= 0) if (fd <= 0)
return 0; return 0;
validate_user_pointer (buffer, size); validate_user_pointer (buffer, size, false);
if (fd == STDOUT_FILENO) if (fd == STDOUT_FILENO)
{ {
@@ -381,20 +388,6 @@ fd_less (const struct hash_elem *a_, const struct hash_elem *b_,
return a->fd < b->fd; 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 /* Gets a file from its descriptor (FD number). If there is no file with the fd
FD it returns NULL. */ FD it returns NULL. */
static struct open_file * static struct open_file *
@@ -414,57 +407,76 @@ fd_get_file (int fd)
return hash_entry (e, struct open_file, elem); return hash_entry (e, struct open_file, elem);
} }
/* Validates if a block of memory starting at START and of size SIZE bytes is /* Validates if a block of memory starting at PTR and of size SIZE bytes is
fully contained within user virtual memory. Kills the thread (by exiting with fully contained within valid user virtual memory. thread_exit () if the
failure) if the memory is invalid. Otherwise, returns (nothing) normally. memory is invalid.
If the size is 0, the function does no checks and returns the given ptr. */ If the size is 0, the function does no checks and returns PTR. */
static void static void
validate_user_pointer (const void *start, size_t size) validate_user_pointer (const void *ptr, size_t size, bool check_write)
{ {
if (size == 0) if (size == 0)
return; return;
/* ptr < ptr + size - 1, so sufficient to check that (ptr + size -1) is a
const void *end = start + size - 1; valid user virtual memory address. */
void *last = ptr + size - 1;
if (start == NULL || !is_user_vaddr (start) || !is_user_vaddr (end)) if (!is_user_vaddr (last))
syscall_exit (EXIT_FAILURE); thread_exit ();
ptr = pg_round_down (ptr);
/* We now need to check if the entire memory block is mapped to physical while (ptr <= last)
memory by the page table. */ {
for (const void *ptr = start; ptr <= end; ptr += PGSIZE) int result;
if (pagedir_get_page (thread_current ()->pagedir, ptr) == NULL) /* Check read access to pointer. */
syscall_exit (EXIT_FAILURE); if ((result = get_user (ptr)) == -1)
thread_exit ();
/* Check write access to pointer (if required). */
if (check_write && !put_user (ptr, result))
thread_exit ();
ptr += PGSIZE;
}
} }
/* Validates if a string is fully contained within user virtual memory. Kills /* Validates of a C-string starting at ptr is fully contained within valid
the thread (by exiting with failure) if the memory is invalid. Otherwise, user virtual memory. thread_exit () if the memory is invalid. */
returns (nothing) normally. */
static void static void
validate_user_string (const char *str) validate_user_string (const char *ptr, bool check_write)
{ {
if (str == NULL || !is_user_vaddr (str)) while (true)
syscall_exit (EXIT_FAILURE); {
if (!is_user_vaddr (ptr))
size_t offset = (uintptr_t) str % PGSIZE; thread_exit ();
int result;
/* We move page by page, checking if the page is mapped to physical memory. */ if ((result = get_user ((const uint8_t *)ptr)) == -1)
for (;;) thread_exit ();
{ if (check_write && !put_user ((uint8_t *)ptr, result))
void *page = pg_round_down (str); thread_exit ();
if (*ptr == '\0')
if (!is_user_vaddr(page) || return;
pagedir_get_page (thread_current ()->pagedir, page) == NULL) ptr++;
syscall_exit (EXIT_FAILURE); }
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. */
}
} }
/* PROVIDED BY SPEC.
Reads a byte at user virtual address UADDR.
UADDR must be below PHYS_BASE.
Returns the byte value if successful, -1 if a segfault occurred. */
static int
get_user (const uint8_t *uaddr)
{
int result;
asm ("movl $1f, %0; movzbl %1, %0; 1:" : "=&a"(result) : "m"(*uaddr));
return result;
}
/* PROVIDED BY SPEC.
Writes BYTE to user address UDST.
UDST must be below PHYS_BASE.
Returns true if successful, false if a segfault occurred. */
static bool
put_user (uint8_t *udst, uint8_t byte)
{
int error_code;
asm ("movl $1f, %0; movb %b2, %1; 1:"
: "=&a"(error_code), "=m"(*udst)
: "q"(byte));
return error_code != -1;
}

View File

@@ -2,18 +2,14 @@
#define USERPROG_SYSCALL_H #define USERPROG_SYSCALL_H
#include <hash.h> #include <hash.h>
#include "threads/synch.h"
#define MIN_USER_FD 2 #define MIN_USER_FD 2
typedef int pid_t; typedef int pid_t;
struct lock filesys_lock;
void syscall_init (void); void syscall_init (void);
unsigned fd_hash (const struct hash_elem *element, void *aux); 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); 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 */ #endif /* userprog/syscall.h */