Search code examples
macosoperating-systemforkosx-elcapitan

OS X Kills processes that fork too quickly?


I'm a TA for an operating systems class where the students are tasked with developing a fork bomb defuser. As part of the test cases I wanted to develop something that looked like a fork bomb, but was in fact fairly safe (i.e. spawns off many processes, but those processes are removed). My question is that on testing this on my OS X machine I noticed that it actually kills all my user processes if I set the usleep delay too low(~100000) and the number of children too high(~1000). When I say all I mean Firefox, Xcode, Word, even Finder seems to go down. This seems somewhat strange to me, as the task only ever has one child, but I am wondering if OS X has a limit to the number of child processes a user can have. I wasn't able to find anything on google, but any suggestions are appreciated.

In particular: 1) Is this code unreasonable and I'm missing some obvious reason it should be killed? 2) Is there some documentation in OS X that might explain the reason we are seeing this behavior?

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

int main(int argc, char **argv)
{
    int i;
    pid_t pid;

    if(argc < 2) {
        printf("Usage: fork_safe n\n");
        return 0;
    }

    int n = strtol(argv[1], NULL, 10);

    for(i = 0; i < n; i++) {
        pid = fork();
        if(pid == 0){
            break;
        } else {
            printf("child pid %d, killing...\n", pid);
            usleep(10000);
            kill(pid,SIGTERM);
            fflush(stdout);
        }
    }

    while(1);
    return 0;
}

Solution

  • You are not checking the return value from fork() for error. And if it returns -1, you're passing -1 into the kill function.

    And according to the man page for the kill function:

    If pid equals -1, then sig is sent to every process for which the calling process has permission to send signals, except for process 1 (init), but see below.

    So I suspect that fork fails when it can't allocate any more processes. Thereby, your code is sending a SIGTERM to EVERY process your account owns. That explains the behavior you are seeing.

    Modify your for-loop accordingly:

    for(i = 0; i < n; i++)
    {
        pid = fork();
        if(pid == 0)
        {
            break;
        }
        else if (pid == -1)
        {
            printf("Unable to allocate any more processes\n");
            return 0;
        }
        else
        {
            printf("child pid %d, killing...\n", pid);
            usleep(10000);
            kill(pid,SIGTERM);
            fflush(stdout);
        }
    }