Implement user pointer checking for C strings

This commit is contained in:
2024-11-12 15:34:45 +00:00
parent 9a6abab95e
commit cf4bf90cbb

View File

@@ -14,6 +14,8 @@
#include <stdbool.h>
#include <syscall-nr.h>
#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,
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.
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 *
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. */
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 ();
return ptr;
}
}
/* 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.