CS360 Final Exam - May 2, 2019 - James S. Plank

Answers and Explanations

Question 1 - 16 points

This is not an official man page -- this would be my answer:

NAME

    fork - create a process

SYNOPSIS

    int fork();

DESCRIPTION

    Fork() creates a new process as a duplicate of the calling process.  The
    new process (the "child") contains an exact copy of the address space,
    registers and open file table of the calling process (the "parent").  As
    such, both processes return from the fork() system call.  The only way that
    the two processes differ is that:

      - The child has a different process id.
      - The child's parent process id is set to the process id of the parent.
      - The return values of the fork() call -- see below.

    All open files in the parent will also be open, with the same file
    descriptors, in the child, as if dup() or dup2() were called.

RETURN VALUES

    - Fork returns 0 in the child.
    - Fork returns the process id of the child in the parent.
    - Fork returns -1 if there is an error; no child process is created.

Grading:


Question 2 - 16 points

This is as nuts-and-bolts as it gets, in jtelnet.c:

#include <pthread.h>
#include "socketfun.h"
#include <stdio.h>
#include <stdlib.h>

/* This thread reads from the socket, and writes to standard output. */

void *thread(void *arg)
{
  char buf[1000];
  FILE *fin;

  fin = (FILE *) arg;
  while (fgets(buf, 1000, fin) != NULL) {
    fputs(buf, stdout);
    fflush(stdout);
  }
  exit(0);
  return NULL;
}

/* This thread reads from standard input, and writes to the socket. */

int main(int argc, char **argv)
{
  int fd;
  FILE *fin, *fout;
  char buf[1000];
  pthread_t tid;

  if (argc != 3) { fprintf(stderr, "usage: telnet host port\n"); exit(1); }
  fd = request_connection(argv[1], atoi(argv[2]));
  
  fin = fdopen(fd, "r");
  fout = fdopen(fd, "w");

  if (pthread_create(&tid, NULL, thread, (void *) fin) != 0) exit(1);

  while (fgets(buf, 1000, stdin) != NULL) {
    fputs(buf, fout);
    fflush(fout);
  }
  return 0;
}

Grading


Question 3 - 16 points

Let's abbreviate it LJUS. LJUS happens when you call setjmp(env), and then you return from the procedure that called setjmp(), and then you subsequently call longjmp(env). An example is below, and in q3.c:

#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>

void a(jmp_buf env)
{
  int i;

  i = 3;
  setjmp(env);
  printf("%d\n", i);
}

int main()
{
  jmp_buf env;

  a(env);
  longjmp(env, 1);
  return 1;
}

The jmp_buf stores the stack and frame pointers of a() at the time that a() is called, but when a() returns, those locations on the stack will be overwritten by any subsequent procedure call. When the longjmp() happens, you'll be running in a()'s old stack frame, whose values will have been overwritten, and are therefore corrupt. This includes the variables of a() and the information that a() uses to return to its caller. In the case of the example above, the longjmp() call itself is what will corrupt the values of a(). When I run it on my Macbook, I get the following:

UNIX> a.out
3
1
Segmentation fault
UNIX> 

Grading

A lot of you tried to BS your way around the answer with things like "LJUS is when you corrupt the stack by improperly calling setjmp() and then longjump() to drastically violate the stack." Sorry, but that didn't get much credit.


Question 4 - 26 points

This is also a pretty nuts-and-bolts question involving fork()/exec()/pipe()/dup()/wait(). In q4.c:

#include "socketfun.h"
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
  int p[2];
  int fd;
  int dummy;

  if (pipe(p) == -1) exit(1);

  if (fork() != 0) {

    if (fork() != 0) {     /* This is the parent, a.out process. */
      close(p[0]);         /* Simply close the pipe and wait for the children to exit. */
      close(p[1]);
      wait(&dummy);
      wait(&dummy);
      exit(0);
    }

    fd = open("f1.txt", O_RDONLY);   /* This is the bin/x child.  First redirect stdin */
    if (fd < 0) exit(1);
    dup2(fd, 0);
    close(fd);

    dup2(p[1], 1);                   /* Then stdout, exec and exit if there was an error. */
    close(p[0]);
    close(p[1]);
    execlp("/bin/x", "x", "4.5", NULL);
    exit(1);
  }

  fd = request_connection("eecs.utk.edu", 6000);   /* And the bin/y child - do stdout. */
  dup2(fd, 1);
  close(fd);

  dup2(p[0], 0);                     /* Then stdin, exec and exit. */
  close(p[0]);
  close(p[1]);
  execlp("/bin/y", "y", "fred", "5", NULL);
  exit(1);
}

Grading


Question 5 - 26 points

If you want to verify, I have a shell script in q5-ss.sh that takes arguments to q5, then runs it 100 times, piping the output to "sort -u". That typically generates all of the possible outputs.