Assignment 2, Solutions

Part of the homework for 22C:169, Spring 2007
by Douglas W. Jones
THE UNIVERSITY OF IOWA Department of Computer Science

  1. Background: Consider an untrusted and untrustworthy program, such as victim from the previous lecture. Assume this program performs some complex and interesting function, but for the sake of this example, assume something like the victim function from the notes for Lecture 5. This victim function just outputs the number that was given to it as input, but in a more interesting setting, it might have performed some interesting, complex and proprietary computation on this number.

    If security were no object, you could just incorporate the code for victim.c into your code. The trouble is, you can't fix the bugs in victim, it's proprietary, the computations it performs are very difficult to understand, and the code is so badly written that you don't dare touch it, and you don't trust it not to threaten your applicaton.

    Therefore, you need to run victim in some kind of sandbox to prevent it from damaging your program. Your goal here is to flesh out this skeleton:

    int launch_victim() {
            /* run victim to get a value from stdin() and return it */
    }
    int main() {
            for (;;) {
                    printf( "Enter number: " );
                    printf( "Victim returned %d\n", launch_victim() );
            }
    }
    

    a) Explain the role of the Unix fork system call in writing launch_victim. In particular, how does fork prevent pointer errors in victim from threatening the launch_victim. (1 point)

    The fork system call allows a process (the parent) to create another process (child). The child process is identical to the parent process in that the variables (at the time of creation of the child) in both processes have the same values, and other resources such as read/write data and stack segments are copied in both proccesses' address spaces.

    Given all this, the advantage the parent has is that operations that were going to be performed in the parent's address space would have similar outcomes if they were to be performed in the child's address space, but since the two address spaces are separate, the outcomes would not affect the parent. Thus if the "victim" program had any potentially harmful effects (such as pointer errors), it would not affect "launch_victim" if both were run as separate processes, created through fork.

    The expected answer to had to be comprised of the contents of the second paragraph. Those people who had varying degrees of explainations were given proportionally fewer or more points.

    b) Explain the role of the Unix pipe system call in writing In particular, how does pipe allow launch_victim to receive data from victim. (1 point)

    Your explaination should have included the following information -

    Pipes in Unix are used as a mechanism by which two processes can communicate with each other. The way it is designed is as a buffer which has two ends, a "read" end and a "write" end. The way it can be used by "launch_victim" and "victim" is that, in stead of letting "victim" print its output to the screen (i.e. to the stream "stdout"), the output can be mapped to the write end of a pipe, so that it can be captured and used by "launch_victim" by reading from the read end of the same pipe.

    Note that, your answer should have been specific to the case of "victim" and "launch_victim" and the streams involved, otherwise full points were not given. This was because the question asked specifically about "victim" and "launch_victim" and otherwise the answer would be vague and would not indicate the extent upto which you were able to understand and solve the problem in 1c.

    c) Write launch_victim. (2 points)

    Some people modified the victim program, some did the opposite of what was required, i.e. made the data being sent from the parent to the child, some others did not actually read data from the read end of the pipe in the parent process but from stdin and there were some others who incorporated the code of "victim" with theirs. All these and others were deviations from the specifications!

    Here is the code for a solution to part 1c.

    /* One_Way_Pipe.c  */
    /********************************************
     * How to use fork() and pipe() for one way *
     * communication between processes.         *
     * Author:  Alankar Kampoowale              *
     ********************************************/
    #include <sys/types.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int launch_victim() {
            /* run victim to get a value from stdin() and return it */
            pid_t pid;
            int ctoppipe[2];  /* the pipe for parent-child communication */
            char readbuf[32]; /* the buffer for reading from the pipe */
    
            int readvalue = 0;
            if (pipe( ctoppipe )) {
                    /* The attempt to create the pipe failed*/
                    fprintf( stderr, "Pipe failed to initialize.\n" );
                    return -1;
            }
    
            if (pid = fork()) { /* parent (nonzero pid) */
                    if (pid < (pid_t) 0) {
                            /* The fork failed. */
                            fprintf (stderr, "Forking the process failed.\n");
                            return -1;
                    }
                    close(ctoppipe[1]); /* close the write end of the pipe */
    
                    /* wait for child to terminate */
                    while (pid != wait( NULL )) /* do nothing */;
            } else { /* child (zero pid) */
                    close(ctoppipe[0]);  /* close the read end of the pipe */
                    dup2(ctoppipe[1],1); /* make the write end standard output */
                    close(ctoppipe[1]);  /* close the former write of the pipe */
    
                    /* execute victim as a separate process */
                    (void) execve( "./victim", NULL, NULL );
    
                    fprintf (stderr, "Exec failed.\n");
                    write(1, "-1", 3); /* write the error return to the pipe */
                    close(1); /* close stdout*/
                    exit(0); /* terminate process */
            }
    
            read(ctoppipe[0], readbuf, 32); /* read from the child */
            /* note:  early child termination will give less than 32 chars */
    
            readvalue = atoi(readbuf); /* convert to integer */
            /* we ought to check for syntax errors in this atoi */
    
            return readvalue; /* the value returned by launch_victim*/
    }
    
    int main() {
            for (;;) {
                    printf( "Enter number:\n " );
                    printf( "Victim returned %d\n", launch_victim() );
            }
    }
    

  2. There are othe potential communication paths between the processes created by the fork system call. Discuss how the shmat system call (and related services) could be used to communicate between your application and the untrusted software it launches. (1 point)

    Your answer should have included the following:

    The "shmat" system call can be used to attach a shared memory segment, identified by the id "shmid" and specified in the call to it, to the calling processes's to the data segment of the calling process. Thus a process (like launch_victim here) can call shmat to get a shared memory segment. It can create anotherprocess (such as to run victim here) using fork and these two processes can then use the shared memory segment to communicate. Thus the address space of the parent stays out of harm's way because child process's potentially operations affect only its address space and the parent can safely collect the results produced by these oprations.

    The most important part here was the description of the use of the shared memory segment i.e. as a shared space where both processes can communicate. Those who got this right got most of the points. This is because, this functionality is specific to the use of shmat in this particular case and distinguishes it from the use of any other mechanism such as pipes, which is what the question asked about.