* file: hmwk1.c author: T.P. Baker date: January 2002 purpose: test for buffer overflows in program simple_fork.c. to compile this program: gcc -o hmwk1 hmwk1.c */ #define _XOPEN_SOURCE 500 #include #include #include #include #include #include #include #include #include #include #define SLEEP_TIME 1 extern char ** environ; /* see "man environ" for explanation */ extern void print_child_status (int status); #define CHILDNAME "simple_fork" int main(int argc, char *argv[]) { pid_t child; int status; int pipefd[2]; char *argument_list_1[] = {"simple_fork", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", NULL}; char *argument_list_2[] = {"simple_fork", NULL}; sigset_t mask; if (argc !=1) { fprintf (stderr, "This program does not accept command-line arguments\n"); exit (-1); } /* create a child process */ fprintf (stderr, "testing with long command-line argument\n"); fflush (NULL); child = fork(); if (child == -1) { /* call to fork failed */ perror ("fork failed"); exit (-1); } if (child != 0) { if (waitpid (child, &status, 0) == -1) { perror ("waitpid failed"); exit (-1); } /* check that child exited with status -1 */ if (!WIFEXITED (status) || !(WEXITSTATUS (status) == -1)) { fprintf (stderr, "*** FAILED *** bad exit status\n"); print_child_status (status); } else { fprintf (stderr, "OK exit status\n"); } /* We should also check that a file was not created using a truncated portion of the argument. To do that would require iterating over different substrings of the test input string, or use of a directory iterator, so we omit that here. */ } else { if (execve (CHILDNAME, argument_list_1, environ) == -1) { perror ("execve failed"); } fprintf (stderr, "execution should never reach here"); } /* create pipe to send output to child One way to send output to a child is to create and open a file, and then redirect stdin to that file, as in the example "hw1.c". Here, we demonsrate another approach, using a "pipe", i.e., a pair of file descriptors that allow one process to send its output as the input to another process. This also allows us to introduce the write() function, which is used for unbuffered output. */ if (pipe (pipefd) == -1) { perror ("could not create pipe"); exit (-1); } fprintf (stderr, "testing with long input line, via pipe\n"); fflush (NULL); child = fork(); if (child != 0) { fprintf (stderr, "This is the parent process, with own pid %d\n", getpid()); close (pipefd[0]); /* the reading end of the pipe */ sigemptyset (&mask); sigaddset (&mask, SIGPIPE); if (sigprocmask (SIG_BLOCK, &mask, NULL) == -1) { perror ("sigprocmask failed"); exit (-1); } if (write (pipefd[1], argument_list_1[1], strlen (argument_list_1[1])) == -1) { perror ("write to pipe failed"); } if (write (pipefd[1], "\n", strlen ("\n")) == -1) { perror ("second write to pipe failed"); } close (pipefd[1]); if (waitpid (child, &status, 0) == -1) { perror ("waitpid failed"); exit (-1); } /* check that child exited with status -1 */ /* check that child exited with status -1 */ if (!WIFEXITED (status) || !(WEXITSTATUS (status) == -1)) { fprintf (stderr, "*** FAILED *** bad exit status\n"); print_child_status (status); } else { fprintf (stderr, "OK exit status\n"); } /* We should also check that a file was not created using a truncated portion of the argument. To do that would require iterating over different substrings of the test input string, or use of a directory iterator, which would make this example quite a bit longer and leave you with less to do for your own solution. */ } else { dup2 (pipefd[0], 0); /* redirect reading end of pipe to stdin */ close (pipefd[0]); if (execve (CHILDNAME, argument_list_2, environ) == -1) { perror ("execve failed"); } fprintf (stderr, "execution should never reach here"); } return 0; }