Compare commits

..

128 Commits

Author SHA1 Message Date
149b0a9d87 Remove filesys CI for gitlab 2024-11-14 03:25:33 +00:00
Saleh Bubshait
79b3b8fda7 Merge branch 'userprog-oom' into 'master'
Fix multi-oom

See merge request lab2425_autumn/pintos_22!39
2024-11-13 22:09:11 +00:00
f5e498e0a9 explicit thread_exit () when process_start () fails 2024-11-13 21:58:41 +00:00
d02b956221 Merge branch 'system-calls' into 'userprog-oom'
Add Fixes to Memory Leaks, Memory Access Validation, Synchronised Processes and Refactoring

See merge request lab2425_autumn/pintos_22!38
2024-11-13 19:20:19 +00:00
Saleh Bubshait
927c376d02 Merge branch 'task2/system-calls/saleh' into 'system-calls'
Fix Memory Leaks, Synchronisation in Processes, and Refactoring

See merge request lab2425_autumn/pintos_22!37
2024-11-13 18:51:51 +00:00
sBubshait
fa2fb4a711 Refactor system call comments for accuracy and grammar 2024-11-13 18:48:23 +00:00
sBubshait
31ea215805 Refactor validate_user_string to remove unnecessary variable to track length of str 2024-11-13 18:30:24 +00:00
e7cb16b301 Fix child_results loop accessing next after free() 2024-11-13 18:29:05 +00:00
sBubshait
9549ca28e5 Refactor syscall: Use EXIT_FAILURE instead of magic numbers & close files on failure 2024-11-13 18:18:41 +00:00
sBubshait
b1c5819469 Avoid masking the struct syscall_arguments using typedef for consistency 2024-11-13 18:06:51 +00:00
sBubshait
287130ca7b Update syscall.c to use syscall_exit on failure instead of calling thread_exit directly 2024-11-13 18:02:08 +00:00
sBubshait
4f586bb4da Fix Bug: Free all entries in the fd hashtable when the process exits, w/ E 2024-11-13 17:42:25 +00:00
sBubshait
d890c2353e Add constant MAX_SYSCALL_ARGS to avoid magic numbers [Gleb] 2024-11-13 17:16:15 +00:00
sBubshait
eb4f23c290 Add validate_user_string helper function to validate that a string is fully conatined within user vm 2024-11-13 17:11:37 +00:00
sBubshait
26de38cdba Update validate_user_pointer to check if the memory block is mapped into a physical address 2024-11-13 16:39:45 +00:00
sBubshait
6e59e8c9f3 Update validate_user_pointer to be a void function 2024-11-13 16:22:16 +00:00
sBubshait
30e49846b5 Add more tests for system calls to deal with bad buffers given to read and write 2024-11-13 16:21:12 +00:00
005791edd2 Synchronise process_execute return with child process load 2024-11-13 11:05:09 +00:00
8bcd0a467c Tidy up a69b9c808e 2024-11-12 21:35:41 +00:00
dd979f34c8 Fix syn-read, syn-write, and always free elements from donors_list 2024-11-12 21:30:23 +00:00
ca9d23edf9 Always release filesys_lock when checking if file is valid in process_execute 2024-11-12 20:07:51 +00:00
Themis Demetriades
d878dbc132 Fix bug in userprog-merge where file writes were denied in the wrong thread 2024-11-12 16:22:32 +00:00
Themis Demetriades
b0400693ae Update process_execute to acquire lock when checking if file exists 2024-11-12 16:12:24 +00:00
Themis Demetriades
3418425f20 Don't acquire filesys lock when calling exec 2024-11-12 16:08:27 +00:00
Themis Demetriades
a69b9c808e Update start_process to acquire filesys lock when loading user process file 2024-11-12 14:21:33 +00:00
Demetriades, Themis
7d9900c6d8 Merge branch 'exec-missing-validation' into 'master'
Add validation to check for missing files in exec() args

See merge request lab2425_autumn/pintos_22!36
2024-11-11 23:25:26 +00:00
EDiasAlberto
72afecfbda Add validation to check for missing files in exec() args 2024-11-11 23:10:02 +00:00
Demetriades, Themis
9e692ced9e Merge branch 'userprog-merge' into 'master'
Update variable references between conflicting merges to refer to the same...

See merge request lab2425_autumn/pintos_22!35
2024-11-11 22:59:45 +00:00
Demetriades, Themis
f194fa1fa8 Merge branch 'userprog-merge' into 'master'
Implement complete stack initialization, process_wait, and all system calls correctly except exec

See merge request lab2425_autumn/pintos_22!34
2024-11-11 22:56:28 +00:00
Themis Demetriades
e8713e26c6 Update variable references between conflicting merges to refer to the same data in stack initialization 2024-11-11 22:51:19 +00:00
Demetriades, Themis
47c3657b65 Merge branch 'process-wait' into 'userprog-merge'
Fix race-condition in process result (memory leak), fix infinite loop in donors_list

See merge request lab2425_autumn/pintos_22!33
2024-11-11 22:24:39 +00:00
Demetriades, Themis
4eafdf10ba Merge branch 'read-only-exec' into 'userprog-merge'
Combine syscall code with final stack initialization code

See merge request lab2425_autumn/pintos_22!32
2024-11-11 22:23:20 +00:00
Themis Demetriades
14a4841772 Fix bug where size of file name buffer was less than maximum file name size 2024-11-11 22:13:10 +00:00
Themis Demetriades
52fdd47e0c Fix race condition in the passing of data from thread executing process_execute to its child 2024-11-11 21:51:38 +00:00
Themis Demetriades
049fc5559c Reformat stack initialization code to follow style for length and spacing 2024-11-11 21:20:53 +00:00
EDiasAlberto
98a5818406 add file_deny_write and file_allow_write to process creation and exiting to make executable read-only 2024-11-11 17:55:24 +00:00
18c884234d Fix race-condition in process result (memory leak), fix infinite loop in donors_list 2024-11-11 17:35:49 +00:00
EDiasAlberto
5b1e4b561b merge process-wait 2024-11-11 15:50:21 +00:00
Themis Demetriades
b8d358ecb2 Update stack initialization to handle overflow by allocating a second page for argument pointers 2024-11-11 13:13:21 +00:00
Themis Demetriades
8b2fc86b51 Refactor process_init_stack to reduce code duplication 2024-11-10 14:34:38 +00:00
Themis Demetriades
324301e7b3 Update process_execute function comment to reflect new function arguments 2024-11-10 13:43:22 +00:00
Themis Demetriades
0ac46db2e4 Refactor process initialization to obtain name of process file in process_execute 2024-11-10 13:41:08 +00:00
Themis Demetriades
795d81b7ad Update process_init_tack saveptr argument name to cmd_savetpr for clarity 2024-11-10 11:33:29 +00:00
Themis Demetriades
6018c0f6ec Refactor process_init_stack to not surpass 80 character limit 2024-11-10 11:25:20 +00:00
Themis Demetriades
f0dae74cf3 Update stack initialization alignment calculation to use WORD_SIZE constant for clarity 2024-11-10 11:24:51 +00:00
Themis Demetriades
a165107f5f Update process execution related function argument names to be more accurate 2024-11-10 11:17:22 +00:00
Themis Demetriades
b37f205334 Update process_init_stack to return success, refactoring error handling to occur inside start_process 2024-11-10 11:13:11 +00:00
EDiasAlberto
1bfd73b202 Comment syscall functions and handlers 2024-11-10 01:32:58 +00:00
7778e05aa4 Fix deadlock by release of lock and semaphore in the wrong order 2024-11-09 11:06:36 +00:00
sBubshait
115c650c55 Fix Bug in thread initialisation: only init hash if USERPROG is defined 2024-11-08 19:10:42 +00:00
Themis Demetriades
5ed999bc9c Refactor push_to_stack helper to match style of other helper functions 2024-11-08 16:53:30 +00:00
sBubshait
e40794e672 Fix Bug in fd_get_file: In case fd not found, then returns NULL, w/ E 2024-11-08 16:48:19 +00:00
Themis Demetriades
a19f02fad7 Move user process stack initialization into a helper function 2024-11-08 16:46:18 +00:00
sBubshait
8912ef4660 Implement writing to file system files in the write system call, w/ E 2024-11-08 16:26:47 +00:00
EDiasAlberto
18694d7b62 Implement file reading syscall and fix fd validation w/ S. 2024-11-08 16:25:49 +00:00
sBubshait
2a1cc3c361 Implement filesize and tell system calls, w/ E 2024-11-08 16:14:53 +00:00
EDiasAlberto
3cfbe198e0 Implement syscall for seek w/ S. 2024-11-08 16:10:46 +00:00
EDiasAlberto
75bd3fbde0 Implement syscall for close() and fix typing bug in fd_get_file w/ S. 2024-11-08 16:02:51 +00:00
Themis Demetriades
b866fa88cd Refactor process.c to use FNAME_MAX_LEN constant 2024-11-08 15:54:47 +00:00
sBubshait
a80084e907 Fix Bug in fd_get_file declaration use open_file instead of file, w/ E 2024-11-08 15:54:28 +00:00
sBubshait
5424276603 Add a helper function to get a file from its descriptor (FD), w/ E 2024-11-08 15:50:48 +00:00
Themis Demetriades
b64434fb9d Move definition of maximum file name length from <syscall> to file.h 2024-11-08 15:41:35 +00:00
EDiasAlberto
92e93b8060 Implement syscall for file opening and refactor open_files initialisation in thread.c w/ S. 2024-11-08 15:33:47 +00:00
sBubshait
5bd94894e0 Update thread structure to add a hash table of open files and initialise it, w/ E 2024-11-08 15:13:04 +00:00
sBubshait
5bbe7a03c0 Add in syscall hash helper functions for open_file struct: fd_hash and fd_less, w/ E 2024-11-08 15:12:08 +00:00
sBubshait
b112824a64 Implement the exec system call through process_execute, w/ E 2024-11-08 14:41:27 +00:00
EDiasAlberto
dca9d8f5a3 Implement syscall for file removal w/ S. 2024-11-08 14:35:23 +00:00
EDiasAlberto
a8676f2e09 Implement syscall for file creation, with relevant locks w/ S. 2024-11-08 14:34:08 +00:00
sBubshait
26e38be761 Update validate_user_pointer to check if the ptr is mapped to a physical memory address, w/ E 2024-11-08 14:21:16 +00:00
6ed1ccd21e Fix process_result locking by acquiring in process_wait as well to prevent freeing memory too early 2024-11-08 10:50:10 +00:00
84fe637c7e Remove process_result lock since it is an invalid solution
TODO : synchronise process_result in another way
2024-11-08 09:16:18 +00:00
d95894085b Implement syscall_exec via process_execute 2024-11-08 09:15:22 +00:00
fde70dcf59 Implement process_wait. 2024-11-08 03:56:15 +00:00
7349b4e66f Fix typo list_insert -> list_push_back for thread.child_results 2024-11-08 03:55:34 +00:00
ec8547aec9 Implement creation of process results 2024-11-08 03:31:48 +00:00
ddcd59fdf8 Add child and own process result information to struct thread 2024-11-08 03:30:58 +00:00
Demetriades, Themis
ed09e0b08e Merge branch 'system-calls' into 'master'
Add support for some basic system calls and args handling correctly.

See merge request lab2425_autumn/pintos_22!29
2024-11-07 19:36:29 +00:00
sBubshait
39018419cd Fix Bug in Process.c to print the exit statement in process_exit instead of exit syscall to handle unexpected/bad exits 2024-11-07 12:15:29 +00:00
sBubshait
6a3cf67d33 Fix Bug in process.c initialising a char to NULL 2024-11-07 11:55:26 +00:00
sBubshait
2bfde66d22 Merge remote-tracking branch 'origin/user-programs-stdout' into system-calls 2024-11-07 11:45:36 +00:00
Themis Demetriades
273fb48b31 Fix stack initialization to pass stack addreses (rather than thread addresses) for the arguments and only pass name a single time 2024-11-07 00:40:52 +00:00
Themis Demetriades
26ae7ac02e Fix bug in stack creation which would count one extra argument for argc 2024-11-06 23:57:48 +00:00
Themis Demetriades
1ca9d09512 Update exit () syscall to print correct termination message 2024-11-06 23:01:10 +00:00
Themis Demetriades
b2764cfa0c Revert setup_stack pointer decrement 'hack' faking stack initialization 2024-11-06 22:46:11 +00:00
Themis Demetriades
4020a140d2 Fix removal of 'timer.h' include needed for calling timer_sleep in process module 2024-11-06 22:36:43 +00:00
Demetriades, Themis
87dd84a9b9 Merge branch 'user-programs' into 'user-programs-stdout'
Merge basic system calls with stack set-up infrastructure

See merge request lab2425_autumn/pintos_22!27
2024-11-06 22:21:28 +00:00
Demetriades, Themis
014642c789 Merge branch 'user-programs-stdout' into 'user-programs'
# Conflicts:
#   src/userprog/process.c
2024-11-06 22:20:48 +00:00
Dias Alberto, Ethan
dfa42b9d25 Merge branch 'user-programs' into 'system-calls'
Implement fake stack and temporary timer change

See merge request lab2425_autumn/pintos_22!26
2024-11-06 16:57:10 +00:00
Dias Alberto, Ethan
1a4afc9ec7 Merge branch 'system-calls' into 'user-programs'
# Conflicts:
#   src/userprog/process.c

Fix conflict in differing placeholder process_wait implementations
2024-11-06 16:56:43 +00:00
Dias Alberto, Ethan
5535cbae24 Merge branch 'master' into 'system-calls'
Merge refactor and removal of duplicated code into section-specific branch

See merge request lab2425_autumn/pintos_22!25
2024-11-06 16:45:53 +00:00
Dias Alberto, Ethan
f685086d05 Merge branch 'refactor-semaphore' into 'master'
Refactor synch.c to remove code duplication in lock release

See merge request lab2425_autumn/pintos_22!23
2024-11-06 16:42:26 +00:00
Dias Alberto, Ethan
2fefdef605 Merge branch 'user-programs-temporary-fix' into 'user-programs'
Temporary fixes for process waiting and stack setup to allow simple user programs to run

See merge request lab2425_autumn/pintos_22!24
2024-11-06 16:41:49 +00:00
sBubshait
fcb7e9e441 Update setup_stack to temporarily fake set-up for a stack to prevent page faults in no arg user programs 2024-11-06 15:48:27 +00:00
sBubshait
ab716de0a6 Update process_wait to temporarily sleep for 1 second to allow user programs to run 2024-11-06 15:46:47 +00:00
sBubshait
91cef4d650 Refactor lock release and sema up to remove unnecessary code 2024-11-06 15:36:56 +00:00
Themis Demetriades
b0c1923d44 Update stack argument initialization behaviour to terminate creation of process on failing memory allocations 2024-11-06 12:28:58 +00:00
sBubshait
5aac37d167 Add temporary fixes to process_wait and setup stack, w/ E 2024-11-05 23:28:17 +00:00
sBubshait
02fff62ca2 Refactor syscall.c to follow PintOS styling, w/ E 2024-11-05 23:24:41 +00:00
EDiasAlberto
f4290c31f3 Implement syscall_read for console input w/ S. 2024-11-05 23:20:18 +00:00
sBubshait
01933cb5de Implement the write system call, w/ E 2024-11-05 23:07:07 +00:00
EDiasAlberto
b3e23eb1cc Implement system call wait w/ S. 2024-11-05 22:48:35 +00:00
EDiasAlberto
421f2c1206 Refactor function names and includes in syscall.c to avoid conflicts w/ S. 2024-11-05 22:46:21 +00:00
sBubshait
e9c4061531 Implement the exit system call, w/ E 2024-11-05 22:38:45 +00:00
sBubshait
2dccd87a76 Update thread to add exit_status, intialised to -1, into the thread structure, w/ E 2024-11-05 22:38:09 +00:00
Themis Demetriades
b4c41b0a6a Remove superfluous include in process.c 2024-11-04 12:57:29 +00:00
Themis Demetriades
2a890d5bd2 Reformat calculation of padding size in stack set-up to increase clarity 2024-11-04 12:54:18 +00:00
Themis Demetriades
6c6ce77824 Implement complete stack initialization for user processes, without accounting for overflow 2024-11-04 01:11:19 +00:00
EDiasAlberto
f8e529e877 Add UNUSED tag to system call function skeletons w/ S. 2024-11-04 01:02:04 +00:00
EDiasAlberto
2a9ab5ec97 fix merge conflicts 2024-11-04 01:00:33 +00:00
EDiasAlberto
4c27aa0203 Complete syscall lookup table, and syscall stubs and skeletons w/ S. 2024-11-04 00:57:19 +00:00
sBubshait
5e2342fad7 Update syscall to make syscall_number an unsigned integer instead of an int 2024-11-04 00:49:47 +00:00
sBubshait
0d057da3dc Refactor syscall to follow PintOS style in adding space after after function name in calls 2024-11-04 00:48:36 +00:00
sBubshait
79f6a8e808 Fix Bug in syscall handler related to pointer arithmetic: add sizeof uintptr_t instead of 1 2024-11-04 00:44:55 +00:00
sBubshait
3a258cf064 Update validate_user_pointer to perform no memory checks when size is 0 2024-11-04 00:38:58 +00:00
sBubshait
e718159ed8 Update syscall to use screaming uppercase casing for a constant 2024-11-04 00:29:07 +00:00
sBubshait
ade8faf0f4 Update syscall to add more comments explaining the basic handler 2024-11-04 00:20:09 +00:00
Themis Demetriades
6f9c911ebe Update start_process to pad process stack before pushing argv elements for performance 2024-11-04 00:19:36 +00:00
Themis Demetriades
62d2cb54e5 Update start_process to push pointers to process arguments onto the process thread's stack 2024-11-04 00:16:04 +00:00
Themis Demetriades
92c681ff02 Reformat start_process stack initialization code to follow style 2024-11-04 00:04:08 +00:00
Themis Demetriades
34d6c15d73 Update start_process to push process argument values to its thread's stack 2024-11-04 00:00:56 +00:00
sBubshait
d626b7a392 Implement basic syscall_handler using the lookup table, w/ E 2024-11-03 23:47:22 +00:00
EDiasAlberto
87126237ad Implement function to validate user memory pointers w/ S. 2024-11-03 23:20:42 +00:00
sBubshait
62453ef432 Add a look up table from system call numbers to their handler functions, w/ E 2024-11-03 22:54:03 +00:00
EDiasAlberto
c0f85a6bcc Implement skeleton for exit command w/ S. 2024-11-03 22:36:22 +00:00
EDiasAlberto
fa6dac2108 Implement halt system call w/ S. 2024-11-03 22:34:50 +00:00
sBubshait
0bf5fdb0e5 Add syscall_function type definition for the different syscall handlers, w/ E 2024-11-03 22:28:17 +00:00
Themis Demetriades
e26b11cce6 Update setup_stack skeleton to fake minimal stack set-up for testing purposes 2024-11-03 18:04:42 +00:00
Themis Demetriades
eb458efa59 Update process_wait skeleton to loop infinitely for testing purposes 2024-11-03 17:38:38 +00:00
23 changed files with 976 additions and 75 deletions

View File

@@ -23,11 +23,6 @@ test_devices:
variables:
DIR: devices
test_filesys:
extends: .pintos_tests
variables:
DIR: filesys
test_threads:
extends: .pintos_tests
variables:

View File

@@ -4,6 +4,9 @@
#include "filesys/off_t.h"
#include <stdbool.h>
/* The maximum length of a file name in PintOS. */
#define FNAME_MAX_LEN 14
struct inode;
/* Opening and closing files. */

View File

@@ -170,6 +170,9 @@ list_insert (struct list_elem *before, struct list_elem *elem)
{
ASSERT (is_interior (before) || is_tail (before));
ASSERT (elem != NULL);
// Sanity checks to prevent (some) loop lists
ASSERT (before != elem);
ASSERT (before->prev != elem);
elem->prev = before->prev;
elem->next = before;

View File

@@ -166,7 +166,7 @@ mkdir (const char *dir)
}
bool
readdir (int fd, char name[READDIR_MAX_LEN + 1])
readdir (int fd, char name[FNAME_MAX_LEN + 1])
{
return syscall2 (SYS_READDIR, fd, name);
}

View File

@@ -3,6 +3,7 @@
#include <stdbool.h>
#include <debug.h>
#include "../../filesys/file.h"
/* Process identifier. */
typedef int pid_t;
@@ -12,9 +13,6 @@ typedef int pid_t;
typedef int mapid_t;
#define MAP_FAILED ((mapid_t) -1)
/* Maximum characters in a filename written by readdir(). */
#define READDIR_MAX_LEN 14
/* Typical return values from main() and arguments to exit(). */
#define EXIT_SUCCESS 0 /* Successful execution. */
#define EXIT_FAILURE 1 /* Unsuccessful execution. */
@@ -41,7 +39,7 @@ void munmap (mapid_t);
/* Task 4 only. */
bool chdir (const char *dir);
bool mkdir (const char *dir);
bool readdir (int fd, char name[READDIR_MAX_LEN + 1]);
bool readdir (int fd, char name[FNAME_MAX_LEN + 1]);
bool isdir (int fd);
int inumber (int fd);

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-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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 <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

@@ -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;

View File

@@ -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 <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

@@ -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;

View File

@@ -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)

View File

@@ -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 <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

@@ -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;

View File

@@ -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)

View File

@@ -113,28 +113,33 @@ void
sema_up (struct semaphore *sema)
{
enum intr_level old_level;
bool thread_unblocked = false; /* Flag to track if any thread was woken up. */
ASSERT (sema != NULL);
old_level = intr_disable ();
if (!list_empty (&sema->waiters))
{
/* Enforces wake-up of the highest priority thread waiting for the
semaphore. */
struct list_elem *e = list_max (&sema->waiters, priority_less, NULL);
list_remove (e);
thread_unblock (list_entry (e, struct thread, elem));
}
{
/* Enforces wake-up of the highest priority thread waiting for the
semaphore. */
struct list_elem *e = list_max (&sema->waiters, priority_less, NULL);
list_remove (e);
thread_unblock (list_entry (e, struct thread, elem));
thread_unblocked = true;
}
sema->value++;
intr_set_level (old_level);
/* Yields the CPU in case the thread that has been woken up has a higher
priority that the current running thread, including the case when called
within an interrupt handler. */
if (intr_context ())
intr_yield_on_return ();
else
thread_yield ();
if (thread_unblocked)
{
if (intr_context ())
intr_yield_on_return ();
else
thread_yield ();
}
}
static void sema_test_helper (void *sema_);
@@ -207,6 +212,7 @@ donate_priority (struct thread *donee) {
ASSERT (intr_get_level () == INTR_OFF);
struct thread *donor = thread_current ();
list_remove (&donor->donor_elem);
list_push_back (&donee->donors_list, &donor->donor_elem);
while (donee != NULL)
@@ -255,6 +261,7 @@ lock_acquire (struct lock *lock)
ASSERT (!lock_held_by_current_thread (lock));
struct thread *t = thread_current ();
ASSERT (t->waiting_lock == NULL);
enum intr_level old_level = intr_disable ();
if (lock->holder != NULL)
@@ -347,7 +354,6 @@ lock_release (struct lock *lock)
lock->holder = NULL;
sema_up (&lock->semaphore);
thread_yield ();
}
/* Returns true if the current thread holds LOCK, false

View File

@@ -1,5 +1,6 @@
#include "threads/thread.h"
#include <debug.h>
#include <hash.h>
#include <stddef.h>
#include <random.h>
#include <stdio.h>
@@ -9,12 +10,14 @@
#include "threads/flags.h"
#include "threads/interrupt.h"
#include "threads/intr-stubs.h"
#include "threads/malloc.h"
#include "threads/palloc.h"
#include "threads/switch.h"
#include "threads/synch.h"
#include "threads/vaddr.h"
#ifdef USERPROG
#include "userprog/process.h"
#include "userprog/syscall.h"
#endif
/* Random value for struct thread's `magic' member.
@@ -68,6 +71,7 @@ static void kernel_thread (thread_func *, void *aux);
static void idle (void *aux UNUSED);
static struct thread *running_thread (void);
static struct thread *next_thread_to_run (void);
static void init_process_result (struct thread *t);
static void init_thread (struct thread *, const char *name, int nice,
int priority, fp32_t recent_cpu);
static bool is_thread (struct thread *) UNUSED;
@@ -110,6 +114,7 @@ thread_init (void)
initial_thread_recent_cpu);
initial_thread->status = THREAD_RUNNING;
initial_thread->tid = allocate_tid ();
initial_thread->result = NULL; /* Main thread cannot be waited for. */
}
/* Starts preemptive thread scheduling by enabling interrupts.
@@ -236,6 +241,11 @@ thread_create (const char *name, int priority,
struct thread *parent_thread = thread_current ();
init_thread (t, name, parent_thread->nice, priority, parent_thread->recent_cpu);
tid = t->tid = allocate_tid ();
init_process_result (t);
#ifdef USERPROG
hash_init (&t->open_files, fd_hash, fd_less, NULL);
#endif
/* Prepare thread for first run by initializing its stack.
Do this atomically so intermediate values for the 'stack'
@@ -259,6 +269,10 @@ thread_create (const char *name, int priority,
intr_set_level (old_level);
/* No need to synchronise child_results since it is only ever accessed by one
thread. By the nature of increasing TIDs, this list is ordered. */
list_push_back (&parent_thread->child_results, &t->result->elem);
/* Add to run queue. */
thread_unblock (t);
@@ -359,7 +373,9 @@ thread_exit (void)
and schedule another process. That process will destroy us
when it calls thread_schedule_tail(). */
intr_disable ();
list_remove (&thread_current()->allelem);
struct thread *t = thread_current ();
list_remove (&t->allelem);
list_remove (&t->donor_elem);
thread_current ()->status = THREAD_DYING;
schedule ();
NOT_REACHED ();
@@ -632,6 +648,18 @@ is_thread (struct thread *t)
return t != NULL && t->magic == THREAD_MAGIC;
}
/* Allocate and initialise a process result for given thread. */
static void
init_process_result (struct thread *t)
{
struct process_result *result = malloc (sizeof (struct process_result));
result->tid = t->tid;
result->exit_status = t->exit_status;
lock_init (&result->lock);
sema_init (&result->sema, 0);
t->result = result;
}
/* Does basic initialization of T as a blocked thread named
NAME. */
static void
@@ -653,12 +681,16 @@ init_thread (struct thread *t, const char *name, int nice, int priority,
t->base_priority
= thread_mlfqs ? calculate_bsd_priority (recent_cpu, nice) : priority;
list_init (&t->donors_list);
list_push_back (&t->donors_list, &t->donor_elem);
t->waiting_lock = NULL;
t->nice = nice;
t->recent_cpu = recent_cpu;
t->priority = t->base_priority;
t->exit_status = -1;
list_init (&t->child_results);
old_level = intr_disable ();
list_push_back (&all_list, &t->allelem);
intr_set_level (old_level);

View File

@@ -2,8 +2,11 @@
#define THREADS_THREAD_H
#include <debug.h>
#include <hash.h>
#include <list.h>
#include <stdint.h>
#include <stdbool.h>
#include "threads/synch.h"
#include "threads/fixed-point.h"
/* States in a thread's life cycle. */
@@ -29,6 +32,18 @@ typedef int tid_t;
#define NICE_DEFAULT 0 /* Default niceness. */
#define NICE_MAX 20 /* Highest niceness. */
/* A process result, synchronised between parent and child. */
struct process_result
{
tid_t tid; /* The tid of the child process. */
int exit_status; /* The exit status of the child process. Initially set
to -1, then to exit_status when child dies. */
struct lock lock; /* Lock the exit_status and sema. */
struct semaphore sema; /* Semaphore to signal the parent that the exit_status
has been set. */
struct list_elem elem; /* List element for the parent's children list. */
};
/* A kernel thread or user process.
Each thread structure is stored in its own 4 kB page. The
@@ -108,13 +123,21 @@ struct thread
int nice; /* Nice value for this thread */
fp32_t recent_cpu; /* Amount of time this process received */
/* Process wait properties. */
struct process_result *result; /* Result of the process. */
struct list child_results; /* List of children's of this thread
process results. */
struct file *exec_file; /* Thread's currently running file */
/* Shared between thread.c and synch.c. */
struct list_elem elem; /* List element. */
int exit_status; /* Exit Status: 0 = successful exit. */
#ifdef USERPROG
/* Owned by userprog/process.c. */
uint32_t *pagedir; /* Page directory. */
struct hash open_files; /* Hash Table of FD -> Struct File */
#endif
/* Owned by thread.c. */

View File

@@ -1,12 +1,14 @@
#include "userprog/process.h"
#include <debug.h>
#include <inttypes.h>
#include <list.h>
#include <round.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "userprog/gdt.h"
#include "userprog/pagedir.h"
#include "userprog/syscall.h"
#include "userprog/tss.h"
#include "filesys/directory.h"
#include "filesys/file.h"
@@ -14,58 +16,165 @@
#include "threads/flags.h"
#include "threads/init.h"
#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 "threads/synch.h"
#include "devices/timer.h"
/* Defines the native number of bytes processed by the processor
(for the purposes of alignment). */
#define WORD_SIZE 4
/* Keeps track of the position of pointers to user program arguments
within a linked list. */
struct arg_elem
{
char* arg;
struct list_elem elem;
};
/* Holds the data required to be passed from a kernel thread to a thread
that executes process_start for the purpose of starting a user process. */
struct process_start_data
{
char *cmd; /* Pointer to a copy of the command used to execute the process.
Allocated a page that must be freed by process_start. */
char *cmd_saveptr; /* Value pointed to by 'saveptr' argument used by
successive calls to strtok_r to split 'cmd' into
tokens while maintaining state. */
char file_name[FNAME_MAX_LEN + 1]; /* Name of the file of the process to
be started. */
bool success; /* Indicates whether the process was successfully loaded. */
struct semaphore loaded; /* Semaphore used to signal that the process has
been loaded. */
};
static thread_func start_process NO_RETURN;
static bool load (const char *cmdline, void (**eip) (void), void **esp);
/* Starts a new thread running a user program loaded from
FILENAME. The new thread may be scheduled (and may even exit)
/* Starts a new thread running a user program executed via
CMD. The new thread may be scheduled (and may even exit)
before process_execute() returns. Returns the new process's
thread id, or TID_ERROR if the thread cannot be created. */
tid_t
process_execute (const char *file_name)
process_execute (const char *cmd)
{
char *fn_copy;
char *cmd_copy;
tid_t tid;
struct process_start_data *data = malloc (sizeof (struct process_start_data));
if (data == NULL)
{
return TID_ERROR;
}
sema_init (&data->loaded, 0);
data->success = false;
/* Make a copy of FILE_NAME.
/* Make a copy of command.
Otherwise there's a race between the caller and load(). */
fn_copy = palloc_get_page (0);
if (fn_copy == NULL)
cmd_copy = palloc_get_page (0);
if (cmd_copy == NULL)
return TID_ERROR;
strlcpy (fn_copy, file_name, PGSIZE);
/* Create a new thread to execute FILE_NAME. */
tid = thread_create (file_name, PRI_DEFAULT, start_process, fn_copy);
/* Imposing implicit limit that the command line arguments
including the user program name fit within a single page. */
strlcpy (cmd_copy, cmd, PGSIZE);
/* Retrieve first argument of command, which is the file name
of the process. */
char *file_name = strtok_r (cmd_copy, " ", &data->cmd_saveptr);
/* 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. */
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;
/* Create a new thread to execute the command, by initializing
it running the function 'start_process' with the appropriate
arguments. For details of arguments, see 'start_process'. */
data->cmd = cmd_copy;
strlcpy (data->file_name, file_name, FNAME_MAX_LEN + 1);
tid = thread_create (file_name, PRI_DEFAULT, start_process, data);
if (tid == TID_ERROR)
palloc_free_page (fn_copy);
palloc_free_page (cmd_copy);
else
{
sema_down (&data->loaded);
if (!data->success)
tid = TID_ERROR;
}
free (data);
return tid;
}
/* A thread function that loads a user process and starts it
running. */
static bool install_page (void *upage, void *kpage, bool writable);
static bool process_init_stack (char *cmd_saveptr, void **esp, char *file_name);
static void *push_to_stack (void **esp, void *data, size_t data_size);
#define push_var_to_stack(esp, var) (push_to_stack (esp, &var, sizeof (var)))
/* Make the current thread execute 'cmd', passing in a copy of the
command string used for processing, the saveptr used by strtok_r
(in order to further tokenize the same command and retrieve its
arguments), as well as the name of the file being executed. This
involves loading the specified file and starting it running. */
static void
start_process (void *file_name_)
start_process (void *proc_start_data)
{
char *file_name = file_name_;
struct intr_frame if_;
bool success;
struct process_start_data *data = proc_start_data;
/* Initialize interrupt frame and load executable. */
memset (&if_, 0, sizeof if_);
if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG;
if_.cs = SEL_UCSEG;
if_.eflags = FLAG_IF | FLAG_MBS;
success = load (file_name, &if_.eip, &if_.esp);
lock_acquire (&filesys_lock);
/* Prevent writing to the file being executed. */
struct file *exec_file = filesys_open (data->file_name);
if (exec_file == NULL)
{
lock_release (&filesys_lock);
goto fail;
}
thread_current ()->exec_file = exec_file;
file_deny_write (exec_file);
lock_release (&filesys_lock);
success = load (data->file_name, &if_.eip, &if_.esp);
/* If load failed, quit. */
palloc_free_page (file_name);
if (!success)
thread_exit ();
{
palloc_free_page (data->cmd);
goto fail;
}
/* Initialize user process stack and free page used to store the
command that executed the process. */
success = process_init_stack (data->cmd_saveptr, &if_.esp, data->file_name);
palloc_free_page (data->cmd);
/* If stack initialization failed, free resources and quit. */
if (!success)
{
goto fail;
}
data->success = true;
sema_up (&data->loaded);
/* Start the user process by simulating a return from an
interrupt, implemented by intr_exit (in
threads/intr-stubs.S). Because intr_exit takes all of its
@@ -74,6 +183,110 @@ start_process (void *file_name_)
and jump to it. */
asm volatile ("movl %0, %%esp; jmp intr_exit" : : "g" (&if_) : "memory");
NOT_REACHED ();
/* If starting the process failed, exit. */
fail:
data->success = false;
sema_up (&data->loaded);
thread_exit ();
}
/* Helper function that initializes the stack of a newly created
user process. Returns true if successful, false otherwise. */
static bool
process_init_stack (char *cmd_saveptr, void **esp, char *file_name)
{
/* 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. */
struct list arg_list;
list_init (&arg_list);
char *arg = file_name;
int arg_count = 0;
while (arg != NULL)
{
push_to_stack (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",
thread_current ()->name);
return false;
}
arg_elem->arg = *esp;
list_push_front (&arg_list, &arg_elem->elem);
arg_count++;
arg = strtok_r (NULL, " ", &cmd_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) *esp % WORD_SIZE) * 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 that is contiguous within the
virtual address space (below the current address range). */
if (PHYS_BASE - *esp + remaining_size > PGSIZE)
{
uint8_t *kpage = palloc_get_page (PAL_USER | PAL_ZERO);
if (!install_page (((uint8_t *) PHYS_BASE) - PGSIZE * 2, kpage, true))
return false;
}
/* Align stack pointer to word size before pushing argv elements for
performance. */
*esp -= align_size;
/* Push a null pointer sentinel inside argv. */
char *null_sentinel = NULL;
push_var_to_stack (esp, null_sentinel);
/* 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_var_to_stack(esp, arg_elem->arg);
e = list_next (e);
free (arg_elem);
}
/* Push pointer to the start of argv array. */
char **argv = *esp;
push_var_to_stack(esp, argv);
/* Push the number of arguments to the stack. */
push_var_to_stack(esp, arg_count);
/* Push fake return address (null pointer). */
push_var_to_stack (esp, null_sentinel);
return true;
}
/* Helper function that pushes the first 'data_size' bytes stored
in the address '*data' into the stack given a pointer to the
stack pointer '**esp'. */
static void *
push_to_stack (void **esp, void *data, size_t data_size)
{
*esp -= data_size;
memcpy (*esp, data, data_size);
return *esp;
}
/* Waits for thread TID to die and returns its exit status.
@@ -88,7 +301,39 @@ start_process (void *file_name_)
int
process_wait (tid_t child_tid UNUSED)
{
return -1;
struct process_result *child_result = NULL;
struct list_elem *e;
struct thread *cur = thread_current ();
for (e = list_begin (&cur->child_results);
e != list_end (&cur->child_results); e = list_next (e))
{
struct process_result *result
= list_entry (e, struct process_result, elem);
if (result->tid == child_tid)
{
child_result = result;
break;
}
/* List is ordered, allowing us to break early. */
else if (result->tid > child_tid)
break;
}
if (child_result == NULL)
return -1;
/* Wait for child to die. */
sema_down (&child_result->sema);
/* We need lock release in process_exit, so we need to acquire (and possibly
wait) for it here to ensure we don't free the lock memory before it is
released in process_exit. */
lock_acquire (&child_result->lock);
/* To prevent waiting for child twice, remove it from the list.
No need to use lock since this is the only thread with access to
the struct process_result now. */
list_remove (&child_result->elem);
int exit_status = child_result->exit_status;
lock_release (&child_result->lock);
free (child_result);
return exit_status;
}
/* Free the current process's resources. */
@@ -98,6 +343,63 @@ process_exit (void)
struct thread *cur = thread_current ();
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);
file_close (cur->exec_file);
lock_release (&filesys_lock);
}
/* Update process result. */
if (cur->result != NULL)
{
lock_acquire (&cur->result->lock);
cur->result->exit_status = cur->exit_status;
/* Parent has died, child has to free the struct process_result * */
if (sema_try_down (&cur->result->sema))
{
lock_release (&cur->result->lock);
free (cur->result);
}
/* Parent is still alive and will be the one to free the
struct process_result *, and may be waiting so call sema_up */
else
{
sema_up (&cur->result->sema);
lock_release (&cur->result->lock);
}
}
/* Free child process results or signal parent's death. */
struct list_elem *e;
for (e = list_begin (&cur->child_results);
e != list_end (&cur->child_results);)
{
struct process_result *result
= list_entry (e, struct process_result, elem);
struct list_elem *next = list_next (e);
lock_acquire (&result->lock);
/* Child has died (and was not waited for). Free the result. */
if (sema_try_down (&result->sema))
{
lock_release (&result->lock);
free (result);
}
/* Child is still alive, signal via sema that parent has died. */
else
{
sema_up (&result->sema);
lock_release (&result->lock);
}
e = next;
}
/* Destroy the current process's page directory and switch back
to the kernel-only page directory. */
pd = cur->pagedir;
@@ -214,6 +516,7 @@ load (const char *file_name, void (**eip) (void), void **esp)
off_t file_ofs;
bool success = false;
int i;
lock_acquire (&filesys_lock);
/* Allocate and activate page directory. */
t->pagedir = pagedir_create ();
@@ -313,13 +616,12 @@ load (const char *file_name, void (**eip) (void), void **esp)
done:
/* We arrive here whether the load is successful or not. */
file_close (file);
lock_release (&filesys_lock);
return success;
}
/* load() helpers. */
static bool install_page (void *upage, void *kpage, bool writable);
/* Checks whether PHDR describes a valid, loadable segment in
FILE and returns true if so, false otherwise. */
static bool

View File

@@ -1,20 +1,470 @@
#include "userprog/syscall.h"
#include "devices/shutdown.h"
#include "devices/input.h"
#include "filesys/file.h"
#include "filesys/filesys.h"
#include "threads/vaddr.h"
#include "threads/interrupt.h"
#include "threads/malloc.h"
#include "threads/thread.h"
#include "threads/synch.h"
#include "userprog/process.h"
#include "userprog/pagedir.h"
#include <stdio.h>
#include <syscall-nr.h>
#include "threads/interrupt.h"
#include "threads/thread.h"
#define MAX_SYSCALL_ARGS 3
#define EXIT_FAILURE -1
static unsigned fd_counter = MIN_USER_FD;
struct open_file
{
int fd; /* File Descriptor / Identifier */
struct file *file; /* Pointer to the associated file */
struct hash_elem elem; /* elem for a hash table */
};
static void syscall_handler (struct intr_frame *);
/* A syscall_function is a function that receives up to 3 arguments, the
arguments to the functions are either ints or pointers taking up to 32 bits
in size. */
typedef uintptr_t (*syscall_function) (uintptr_t, uintptr_t, uintptr_t);
/* System call function prototypes */
static void syscall_halt (void);
static void syscall_exit (int status);
static pid_t syscall_exec (const char *cmd_line);
static int syscall_wait (pid_t pid);
static bool syscall_create (const char *file, unsigned initial_size);
static bool syscall_remove (const char *file);
static int syscall_open (const char *file);
static int syscall_filesize (int fd);
static int syscall_read (int fd, void *buffer, unsigned size);
static int syscall_write (int fd, const void *buffer, unsigned size);
static void syscall_seek (int fd, unsigned position);
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 *start, size_t size);
static void validate_user_string (const char *str);
/* A struct defining a syscall_function pointer along with its arity. */
struct syscall_arguments
{
syscall_function function; /* Function pointer. */
int arity; /* Number of arguments of the function. */
};
/* A look-up table mapping numbers to system call functions with their number of
arguments. */
static const struct syscall_arguments syscall_lookup[] =
{
[SYS_HALT] = {(syscall_function) syscall_halt, 0},
[SYS_EXIT] = {(syscall_function) syscall_exit, 1},
[SYS_EXEC] = {(syscall_function) syscall_exec, 1},
[SYS_WAIT] = {(syscall_function) syscall_wait, 1},
[SYS_CREATE] = {(syscall_function) syscall_create, 2},
[SYS_REMOVE] = {(syscall_function) syscall_remove, 1},
[SYS_OPEN] = {(syscall_function) syscall_open, 1},
[SYS_FILESIZE] = {(syscall_function) syscall_filesize, 1},
[SYS_READ] = {(syscall_function) syscall_read, 3},
[SYS_WRITE] = {(syscall_function) syscall_write, 3},
[SYS_SEEK] = {(syscall_function) syscall_seek, 2},
[SYS_TELL] = {(syscall_function) syscall_tell, 1},
[SYS_CLOSE] = {(syscall_function) syscall_close, 1},
};
/* The number of syscall functions (i.e, number of elements) within the
syscall_lookup table. */
static const int LOOKUP_SIZE
= 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. */
void
syscall_init (void)
{
intr_register_int (0x30, 3, INTR_ON, syscall_handler, "syscall");
lock_init (&filesys_lock);
}
/* 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
syscall_handler (struct intr_frame *f)
{
/* First, read the system call number from the stack. */
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)
syscall_exit (EXIT_FAILURE);
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),
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));
/* Call the function that handles this system call with the arguments. When
there is a return value it is stored in f->eax. */
f->eax = syscall.function (args[0], args[1], args[2]);
}
/* Called upon a "halt" syscall, resulting in a complete shutdown of the
process, via shutdown_power_off (); */
static void
syscall_halt (void)
{
shutdown_power_off ();
}
static void
syscall_handler (struct intr_frame *f UNUSED)
syscall_exit (int status)
{
printf ("system call!\n");
/* Sets exit_status of the thread to status. thread_exit () will call
process_exit () if user programs are allowed. */
thread_current ()->exit_status = status;
thread_exit ();
}
/* Executes a given command with the relevant args, by calling process_execute.
Returns PID for the process that is running the CMD_LINE. */
static pid_t
syscall_exec (const char *cmd_line)
{
validate_user_string (cmd_line);
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
necessary validation and such all happens in process_wait anyway. */
static int
syscall_wait (pid_t pid)
{
return process_wait (pid); /* Returns the exit status of the waited process */
}
/* Handles the syscall for file creation. First validates the user file
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, unsigned initial_size)
{
validate_user_string (file);
lock_acquire (&filesys_lock);
bool status = filesys_create (file, initial_size);
lock_release (&filesys_lock);
return status;
}
/* Handles the syscall for file removal. First validates the user file pointer.
Acquires the file system lock to prevent synchronisation issues, and then
uses FILESYS_REMOVE to remove the file, returning the same success status */
static bool
syscall_remove (const char *file)
{
validate_user_string (file);
lock_acquire (&filesys_lock);
bool status = filesys_remove (file);
lock_release (&filesys_lock);
return status;
}
/* Handles the syscall for opening a file connection. First, validates the file
pointer. Then it acquires a lock for the file system, in order to open the
connection without synchronisation issues. It then maps a new fd to this file
in the hash table before returning the fd. */
static int
syscall_open (const char *file)
{
validate_user_string (file);
lock_acquire (&filesys_lock);
struct file *ptr = filesys_open (file);
lock_release (&filesys_lock);
if (ptr == NULL)
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)
{
file_close (ptr);
return EXIT_FAILURE;
}
/* Populate the above struct, with a unique FD and the current open file */
file_info->fd = fd_counter++;
file_info->file = ptr;
/* Add the new FD->file mapping to the hashtable for the current thread */
hash_insert (&thread_current ()->open_files, &file_info->elem);
/* Return the new FD */
return file_info->fd;
}
/* Handles the syscall for getting a file's size. Converts a provided FD into
the asssociated file struct. Acquire the lock for the filesystem and use
FILE_LENGTH to calculate the length for return. */
static int
syscall_filesize (int fd)
{
struct open_file *file_info = fd_get_file (fd);
if (file_info == NULL)
return EXIT_FAILURE;
lock_acquire (&filesys_lock);
int bytes = file_length (file_info->file);
lock_release (&filesys_lock);
return bytes;
}
/* Handles the syscall for reading SIZE bytes from a file referenced by FD.
If the FD references the console, use input_getc (), otherwise convert the
FD to its associated file struct, acquire the filesystem lock, read up to
SIZE bytes and then return the number of bytes read.*/
static int
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 EXIT_FAILURE;
validate_user_pointer (buffer, size);
if (fd == STDIN_FILENO)
{
/* Reading from the console. */
char *write_buffer = buffer;
for (int i = 0; i < size; i++)
write_buffer[i] = input_getc ();
return size;
}
else
{
/* Reading from a file. */
struct open_file *file_info = fd_get_file (fd);
if (file_info == NULL)
return EXIT_FAILURE;
lock_acquire (&filesys_lock);
int bytes_written = file_read (file_info->file, buffer, size);
lock_release (&filesys_lock);
return bytes_written;
}
}
/* Handles the syscall for writing SIZE bytes to a file referenced by FD.
If the FD references the console, use put_buf (), otherwise convert the
FD to its associated file struct, acquire the filesystem lock, write up to
SIZE bytes and then return the number of bytes written.*/
static int
syscall_write (int fd, const void *buffer, unsigned size)
{
/* Only console (fd = 1) or other files, not including STDIN, (fd > 1) are
allowed. */
if (fd <= 0)
return 0;
validate_user_pointer (buffer, size);
if (fd == STDOUT_FILENO)
{
/* Writing to the console. */
putbuf (buffer, size);
return size;
}
else
{
/* Writing to a file. */
struct open_file *file_info = fd_get_file (fd);
if (file_info == NULL)
return 0;
lock_acquire (&filesys_lock);
int bytes = file_write (file_info->file, buffer, size);
lock_release (&filesys_lock);
return bytes;
}
}
/* Handles the syscall for seeking to POSITION bytes in a file referenced by
FD. Converts the FD to its associated file struct, acquires the filesystem
lock and then uses file_seek to adjust the cursor to a specific position in
the file.*/
static void
syscall_seek (int fd, unsigned position)
{
struct open_file *file_info = fd_get_file (fd);
if (file_info != NULL)
{
lock_acquire (&filesys_lock);
file_seek (file_info->file, position);
lock_release (&filesys_lock);
}
}
/* Handles the syscall for returning the next byte in a file referenced by
FD. Converts the FD to its associated file struct, acquires the filesystem
lock and then uses file_tell to read the next byte.*/
static unsigned
syscall_tell (int fd)
{
struct open_file *file_info = fd_get_file (fd);
if (file_info == NULL)
return 0;
lock_acquire (&filesys_lock);
unsigned pos = file_tell (file_info->file);
lock_release (&filesys_lock);
return pos;
}
/* Handles the syscall for closing a connection to a file. Converts the FD to
its associated file struct. If it exists, it removes it from the hash table,
acquires the filesystem lock, and uses file_close to close the connection.*/
static void
syscall_close (int fd)
{
struct open_file *file_info = fd_get_file (fd);
if (file_info != NULL)
{
hash_delete (&thread_current ()->open_files, &file_info->elem);
lock_acquire (&filesys_lock);
file_close (file_info->file);
lock_release (&filesys_lock);
free (file_info);
}
}
/* Hashing function needed for the open_file table. Returns a hash for an entry,
based on its FD. */
unsigned
fd_hash (const struct hash_elem *element, void *aux UNUSED)
{
return hash_int (hash_entry (element, struct open_file, elem)->fd);
}
/* Comparator function for the open_file table. Compares two entries based on
the FDs. */
bool
fd_less (const struct hash_elem *a_, const struct hash_elem *b_,
void *aux UNUSED)
{
struct open_file *a = hash_entry (a_, struct open_file, elem);
struct open_file *b = hash_entry (b_, struct open_file, elem);
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 *
fd_get_file (int fd)
{
/* We have to set up a fake open_file in order to be able to search the hash
table. See hash.h. */
struct open_file fake_file_info;
fake_file_info.fd = fd;
struct hash_elem *e
= hash_find (&thread_current ()->open_files, &fake_file_info.elem);
if (e == NULL)
return NULL;
return hash_entry (e, struct open_file, elem);
}
/* 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 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)
{
if (size == 0)
return;
const void *end = start + size - 1;
if (start == NULL || !is_user_vaddr (start) || !is_user_vaddr (end))
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)
syscall_exit (EXIT_FAILURE);
}
/* Validates if a string is fully contained within user virtual memory. Kills
the thread (by exiting with failure) if the memory is invalid. Otherwise,
returns (nothing) normally. */
static void
validate_user_string (const char *str)
{
if (str == NULL || !is_user_vaddr (str))
syscall_exit (EXIT_FAILURE);
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);
if (!is_user_vaddr(page) ||
pagedir_get_page (thread_current ()->pagedir, page) == NULL)
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. */
}
}

View File

@@ -1,6 +1,19 @@
#ifndef USERPROG_SYSCALL_H
#define USERPROG_SYSCALL_H
#include <hash.h>
#include "threads/synch.h"
#define MIN_USER_FD 2
typedef int pid_t;
struct lock filesys_lock;
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 */