CSE4001 Fall 2005 Final Exam, open book, open notes. Name _________________ 1. Write UNIX commands to (5 pts each): ANSWER Make all files in your home directory readable only by you and not writable or executable by anyone. chmod 400 ~/* Display the path. echo $PATH Compile foo.c in the background with standard csh: gcc foo.c >& /dev/null & output and standard error discarded. bash: gcc foo.c > /dev/null 2>&1 & 2. What causes a segmentation fault? (5 pts) ANSWER: when a process reads, writes, or executes memory outside of its assigned segments. 3. What are the two operations on semaphores and when are they used? (10 pts) ANSWER: wait - when a thread enters a critical area (where only one thread can be at a time). signal - when a thread leaves a critical area. 4. A suid root program contains the following code. What vulnerability is there? Explain step by step how a malicious user can gain root privileges without knowing the password. (20 pts) int main(int argc, char **argv) { char buf[100] = ""; if (argc > 1) strcpy(buf, argv[1]); /* do more stuff */ } ANSWER. There is a buffer overflow vulnerability. An attacker can pass as the first argument a long string containing machine instructions that are to be executed with root privileges. The argument is long enough (over 100 bytes) to overwrite the return address on the stack, replacing it with a pointer into buf. When main() returns, the code is executed. The code could be the machine equivalent of execl("/bin/sh",0) to open a root shell. 5. A certain computer has 64MB of RAM. The virtual memory system uses 4KB pages and LRU replacement. Assume that no other processes are running and the program is already in memory, occupying 10 pages. (20 pts) const int N = 0x10000000; // 256 MB char *p = malloc(N); // allocate uninitialized array (assume page aligned) memset(p, ' ', N); // fill with spaces memcmp(p, p, N); // compare with self How many page faults are there? (assume malloc() causes no page faults) ANSWER: 128K (131,072). There are 64K pages in the array (256MB/4KB). Each page faults twice, once in memset() and once in memcmp(). In memset() all of the pages fault once because none are initially in memory. As memcmp() sweeps through the array, the oldest page is always the next one to be accessed, so it has already been swapped out. How many pages are written to disk? ANSWER: 64K (65,536). By the time each page faults the second time, it has already been written by memset(). Pages swapped out by memcmp() do not need to be written, so each page is written to disk exactly once. During memset() the array looks like this: +------------------+------------+------------------+ | written to disk | in memory | not yet accessed | +------------------+------------+------------------+ ^ current element -------+ pointer sweeps to right ----> At the end of memset() +-------------------------------------+------------+ | written to disk | in memory | +-------------------------------------+------------+ Near the beginning of memcmp() +--------+-----------------------------------+-----+ | memory | written to disk | mem | +--------+-----------------------------------+-----+ ^ -----+ Middle of memcmp() +-------------------------+-----------+------------+ | discarded (not written) | in memory | on disk | +-------------------------+-----------+------------+ ^ -----+ End of memcmp() +--------------------------------------+-----------+ | discarded | in memory | +--------------------------------------+-----------+ 6. Write a C or C++ program that takes a command and its arguments as arguments, runs the command piped into sort, and returns the status of that command. For example: ./a.out diff file1 file2 is equivalent to: diff file1 file2 | sort and returns the return status of diff (30 pts). // ANSWER: Fork 2 children, one to run the command (not necessarily diff) // and the second to sort. Pipe the first to the second. Parent waits // for both children, identifies the first child by its pid and // returns its status. int main(int argc, char **argv) { int pid1, fd[2], status; pipe(fd); if ((pid1 = fork()) && fork()) { // parent close(fd[0]); close(fd[1]); if (wait(&status) != pid1) wait(&status); return status >> 8; // of first child } else if (pid1) { // first child, pipe command output close(fd[0]); dup2(fd[1], 1); close(fd[1]); execvp(argv[1], argv+1); } else { // second child, sort from pipe close(fd[1]); dup2(fd[0], 0); close(fd[0]); execlp("sort", "sort", 0); } }