Undeterministic C Behavior?"Fork bomb" - c

So I created a "fork" bomb per say. However when I run it on my computer it kills everything on my computer, goes to black screen then restores itself.
On my friends computer when running the same exact code, his actually does a fork bomb but never makes it to the kill loop.
Any reason why?
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
int main(){
int pc = 0;
int* pids = calloc(1025,sizeof(int));
L1:
while(1){
int pid = fork();
if(pid != 0)
{
pc++;
pids[pc] = pid;
}
if(pc == 1024)
{
goto L2;
break;
}
}
L2:
while(1){
if(pids[pc] != 0) {
kill(pids[pc],SIGKILL);
}
if(pc == 0)
{
goto L1;
break;
}
pc--;
}
free(pids);
}
Note this code is just for funsies.
Update:
putting pc++. outside of the if statement caused a kernel panic. Could someone explain to me why?
In theory this code doesn't even work.

The reason you're probably crashing is that it's possible for fork() to fail, in which case it will return -1. When you call kill(-1, SIGKILL), it sends SIGKILL to every process on your system. If you're running as a privileged user, the reason this is terrible should be obvious.
Side notes:
The return type of fork() is pid_t, not int. In most cases, pid_t happens to fit in an int, but you should use the proper types.
It's pointless to have a break statement after a goto statement. The break can never be reached.
If you enabled warnings on your compiler, it probably would have told you about both of those.

"fork bomb", by its nature, can't have any deterministic behaviour. In theory, a computer with infinite resources can keep on forking without any problem.
But in practice, we know computers don't have infinite resources. So, different operating systems might handle the resource drain in different ways.
Typically, when the operating system can't spawn further processes, the kernel might kill the "offending" process(es) in order to free up resources or crash or get into a limbo state. The exponential growth of processes is generally hard to handle for the kernel even if it recognizes it.
So, you just can't expect anything deterministic or repeatable behaviour.

Related

Where does the process start to execute after fork()

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void) {
for (int i = 1; i < 4; i++) {
printf("%d", i);
int id = fork();
if (id == 0) {
printf("Hello\n");
exit(0);
} else {
exit(0);
}
}
return 0;
}
For this code, it prints 11Hello on my computer. It seems counter-intuitive to me because "1" is printed twice but it's before the fork() is called.
The fork() system call forks a new process and executes the instruction that follows it in each process parallelly. After your child process prints the value of i to the stdout, it gets buffered which then prints the value of 'i' again because stdout was not flushed.
Use the fflush(stdout); so that 'i' gets printed only once per fork.
Alternately, you could also use printf("%d\n", i); where the new line character at the end does the job.
Where does the process start to execute after fork()
fork() duplicates the image of the process and it's context. It will run the next line of code pointed by the instruction pointer.
It seems counter-intuitive to me because "1" is printed twice but it's before the fork() is called.
Read printf anomaly after "fork()"
To begin with, the for loop is superfluous in your example.
Recall that the child copies the caller's memory(that of its parent) (code, globals, heap and stack), registers, and open files. To be performant or there may be some other reason, the printf call may not flush the buffer and put the things passed to that except for some cases such as appending new-line-terminator.
Before forking, the parent(main process) is on the way.
Let's assume we're on a single core system and the child first preempts the core.
1 is in the buffer because its parent put it into that before forking. Then, the child reaches second print statement, a caveat here is that the child can be orphaned at that time(no matter for this moment), passing "Hello\n" string including new-line character giving rise to dump the buffer/cache(whatever you call.) Since it sees \n character, it flushes the buffer including prior 1 added by its parent, that is 11Hello.
Let's assume the parent preempts the core at first,
It surrenders after calling exit statement, bringing on the child to be orphaned, causing memory leak. After that point, the boss(init possessing process id as 1) whose newly name I forget(it may be sys-something) should handle this case. However, nothing is changed as to the printing-steps. So you run into again 11Hello except if not the buffer is flushed automagically.
I don't have much working experience with them but university class(I failed at the course 4 times). However, I can advise you whenever possible use stderr while coping with these tings since it is not buffered, in lieu of stdout or there is some magical way(I forget it again, you call it at the beginning in main()) you can opt for to disable buffering for stdout as well.
To be more competent over these topics, you should glance at The Linux Programming Interface of Michael Kerrisk and the topics related to William Pursell,
Jonathan Leffler,
WhozCraig,
John Bollinger, and
Nominal Animal. I have learnt a plethora of information from them even if the information almost wholly is useless in Turkey borders.
*Magic means needing a lot of details to explain.

Can I fork processes and exec internal functions?

I've really searched for this and all I can find is that you can execvp() shell commands.
I'm wondering if I can fork processes and then have them run a function that's internal to the program? (As in, a function I've written myself within the code)
Of course you can have the child execute one function and the parent execute a different (or even the same) function in the same executable.
pid_t pid = fork();
if (pid < 0)
err_syserr("failed to fork: ");
else if (pid == 0)
be_childish();
else
be_parental();
You can add arguments to be_childish() and be_parental() as needed. Before the code executes fork(), you can create pipes or sockets to communicate between them — or semaphores, and shared memory, or whatever IPC you want.
When you call fork, a new process is created inheriting the current context of the parent process. The child and parent processes can execute independently calling any function within your program. However, if they need to communicate/synchronize with each other, they need to use one of the IPC mechanisms such as shared memories, pipes, semaphores etc.
I suspect the simplest answer here is an example:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
static int child_foo(void)
{
/* This child process just counts to three. */
int i;
for (i = 1; i <= 3; i++) {
printf("Foo: %d\n", i);
fflush(stdout);
sleep(1);
}
printf("Foo is done.\n");
return EXIT_SUCCESS;
}
static int child_bar(unsigned long n)
{
/* This child process checks if n is prime or not. */
const unsigned long imax = (n + 1) / 2;
unsigned long i;
for (i = 2; i <= imax; i++)
if (!(n % i)) {
printf("Bar: Because %lu is divisible by %lu, %lu is not a prime.\n", n, i, n);
return EXIT_FAILURE;
}
printf("Bar: %lu is prime.\n", n);
return EXIT_SUCCESS;
}
int main(void)
{
pid_t p, foo, bar;
int status;
printf("Forking child processes.\n");
fflush(stdout);
foo = fork();
if (foo == -1) {
fprintf(stderr, "Cannot fork: %s.\n", strerror(errno));
return EXIT_FAILURE;
} else
if (!foo)
return child_foo();
bar = fork();
if (bar == -1) {
fprintf(stderr, "Cannot fork: %s.\n", strerror(errno));
/* Wait until all child processes (here, foo only) have completed. */
do {
p = wait(NULL);
} while (p != -1 || errno == EINTR);
return EXIT_FAILURE;
} else
if (!bar)
return child_bar(227869319);
/* Wait until all child processes have completed. */
do {
p = wait(&status);
if (p == foo || p == bar) {
/* Report exit status. */
if (p == foo)
printf("child_foo()");
else
printf("child_bar()");
if (WIFEXITED(status)) {
if (WEXITSTATUS(status) == EXIT_SUCCESS)
printf(" exited successfully (EXIT_SUCCESS).\n");
else
if (WEXITSTATUS(status) == EXIT_FAILURE)
printf(" exited with failure (EXIT_FAILURE).\n");
else
printf(" exited with status %d.\n", WEXITSTATUS(status));
} else
if (WIFSIGNALED(status))
printf(" died from signal %d (%s).\n", WTERMSIG(status), strsignal(WTERMSIG(status)));
else
printf(" was lost.\n");
fflush(stdout);
}
} while (p != -1 || errno == EINTR);
printf("All done.\n");
return EXIT_SUCCESS;
}
Above, main() forks two child processes. One of them runs child_foo() and exits, the other runs child_bar(227869319) and exits. The parent process reaps all child processes, and returns the cause of exit and exit status if available, then exits itself as well.
The fflush(stdout) before the fork() is there to remind that internal caches (including caching done by the C library) should be flushed before forking, or else the child process will inherit the cache contents. In the case of stdout, this would mean duplicated outputs.
In practice, it is often better for maintainability (human programmer understanding) to "return" from the child process functions using exit(EXIT_SUCCESS) or exit(EXIT_FAILURE), than return. Then, in main(), instead of say return child_foo(), you'd have say
child_foo(); /* Never returns. */
exit(EXIT_FAILURE);
where the exit(EXIT_FAILURE) is just a bug catcher, in case a modification to child_foo() causes it to return rather than exit.
In some cases the return pattern is useful, if there is cleanup work that must always be done before the child process exits. In that case, you usually put that cleanup work in a separate function, and then replace return child_foo() with
int exitstatus;
exitstatus = child_foo();
cleanup_function();
exit(exitstatus);
Note that in main(), return exitstatus; and exit(exitstatus); are exactly equivalent (C89 2.1.2.2, C99 5.1.2.2.3p1, C11 5.1.2.2.3p1). The only difference is to us humans; it may be easier for us humans to correctly interpret the intent behind exit(exitstatus) in the middle of main(), compared to return exitstatus;. Myself, I "parse" them the same, but I seem to use return more.
The strsignal() function is defined in POSIX-1.2008, and is only called if you manage to kill one of the child processes with an external signal without killing the parent as well. If your C library does not support it, just remove it (also remove the %s printf specifier, too).
As usual, the second most important thing to do, is to ensure future developers understand the intent correctly. I included pretty minimal comments, but both the function names and the initial comment in the child process functions should make the intent clear. I'm sure you can do even better. (I do consider commenting to be the hardest part in programming for me. It is so easy to write useless comments that describe what the code does, but omit the intent. When you look at even your own code months later, you forget your thoughts then, and must rely on the comments (and indirectly infer from the code) to get a proper understanding of the intent. Good comments are hard to write!)
In my opinion, the most important thing is to ensure the program is robust, and does not silently corrupt data. This means sufficient error checking (in my case, paranoid -- I really don't want to silently corrupt data) and minimal assumptions on what "ought to" succeed. Sometimes, it leads to "nests" of code, like the status checks in the wait() loop in main(). I deliberately left out the comments there, because I believe you should go through it, keeping a browser or terminal window open to man 2 wait, and add the necessary comments so you understand exactly what that nest of code does. It will definitely help you understand a lot about how processes can terminate or be terminated, and how a parent process can detect that.
You fork a process (a running, active, instance of a program) and you execve an executable (a passive file, usually in ELF format) - not some functions. Read again fork(2) and execve(2) and credentials(7). See also Operating Systems: Three Easy Pieces to understand better the role of an OS.
that you can execvp() shell commands.
Wrong. execvp calls execve and runs executables (not shell commands; e.g. you cannot execvp the cd shell builtin). It does not use the shell (but execvp search your PATH variable as your shell does).
Notice that every process has its own virtual address space. And fork creates a new process with its own new virtual address space (which happens to be a copy of the virtual address space of the parent. That copy happens lazily, read about copy-on-write techniques). execve is replacing the virtual address process by a new one (described in the executable file).
Since that new virtual address space (after a successful fork, in the child process) is a copy of the parent one, it also contains the code of every internal function you dream of from your program. So after a fork you can call these internal functions.
Use pmap(1) and proc(5) to understand the virtual address space of processes. At first, run cat /proc/$$/maps then pmap $$ in a terminal, and try to understand its output (it describes the virtual address space of your shell process).
When a process is running, you could extend its virtual address space using mmap(2). This is used by malloc, and also by dlopen(3) (which enables you to load plugins into your process).
PS. I am guessing you are using Linux.

C-program does not return from wait-statement

I have to migrate a C-program from OpenVMS to Linux, and have now difficulties with a program generating subprocesses. A subprocess is generated (fork works fine), but execve fails (which is correct, as the wrong program name is given).
But to reset the number of active subprocesses, I afterwards call a wait() which does not return. When I look at the process via ps, I see that there are no more subprocesses, but wait() does not return ECHILD as I had thought.
while (jobs_to_be_done)
{
if (running_process_cnt < max_process_cnt)
{
if ((pid = vfork()) == 0)
{
params[0] = param1 ;
params[1] = NULL ;
if ((cstatus = execv(command, params)) == -1)
{
perror("Child - Exec failed") ; // this happens
exit(EXIT_FAILURE) ;
}
}
else if (pid < 0)
{
printf("\nMain - Child process failed") ;
}
else
{
running_process_cnt++ ;
}
}
else // no more free process slot, wait
{
if ((pid = wait(&cstatus)) == -1) // does not return from this statement
{
if (errno != ECHILD)
{
perror("Main: Wait failed") ;
}
anz_sub = 0 ;
}
else
{
...
}
}
}
Is the anything that has to be done to tell the wait-command that there are no more subprocesses?
With OpenVMS the program works fine.
Thanks a lot in advance for your help
I don't recommend using vfork these days on Linux, since fork(2) is efficient enough, thanks to lazy copy-on-write techniques in the Linux kernel.
You should check the result of fork. Unless it is failing, a process has been created, and wait (or waitpid(2), perhaps with WNOHANG if you don't want to really wait, but just find out about already ended child processes ...) should not fail (even if the exec function in the child has failed, the fork did succeed).
You might also carefully use the SIGCHLD signal, see signal(7). A defensive way of using signals is to set some volatile sigatomic_t flag in signal handlers, and test and clear these flags inside your loop. Recall that only async signal safe functions (and there are quite few of them) can be called -even indirectly- inside a signal handler. Read also about POSIX signals.
Take time to read Advanced Linux Programming to get a wider picture in your mind. Don't try to mimic OpenVMS on POSIX, but think in a POSIX or Linux way!
You probably may want to always waitpid in your loop, perhaps (sometimes or always) with WNOHANG. So waitpid should not be only called in the else part of your if (running_process_cnt < max_process_cnt) but probably in every iteration of your loop.
You might want to compile with all warnings & debug info (gcc -Wall -Wextra -g) then use the gdb debugger. You could also strace(1) your program (probably with -f)
You might want to learn about memory overcommitment. I dislike this feature and usually disable it (e.g. by running echo 0 > /proc/sys/vm/overcommit_memory as root). See also proc(5) -which is very useful to know about...
From man vfork:
The child must not return from the current function or call exit(3), but may call _exit(2)
You must not call exit() when the call to execv (after vfork) fails - you must use _exit() instead. It is quite possible that this alone is causing the problem you see with wait not returning.
I suggest you use fork instead of vfork. It's much easier and safer to use.
If that alone doesn't solve the problem, you need to do some debugging or reduce the code down until you find the cause. For example the following should run without hanging:
#include <sys/wait.h>
int main(int argc, char ** argv)
{
pid_t pid;
int cstatus;
pid = wait(&cstatus);
return 0;
}
If you can verify that this program doesn't hang, then it must be some aspect of your program that is causing a hang. I suggest putting in print statements just before and after the call to wait.

How and why can fork() fail?

I'm currently studying the fork() function in C. I understand what it does (I think). Why do we check it in the following program?
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int pid;
pid=fork();
if(pid<0) /* Why is this here? */
{
fprintf(stderr, "Fork failed");
exit(-1);
}
else if (pid == 0)
{
printf("Printed from the child process\n");
}
else
{
printf("Printed from the parent process\n");
wait(pid);
}
}
In this program we check if the PID returned is < 0, which would indicate a failure. Why can fork() fail?
From the man page:
Fork() will fail and no child process will be created if:
[EAGAIN] The system-imposed limit on the total number of pro-
cesses under execution would be exceeded. This limit
is configuration-dependent.
[EAGAIN] The system-imposed limit MAXUPRC (<sys/param.h>) on the
total number of processes under execution by a single
user would be exceeded.
[ENOMEM] There is insufficient swap space for the new process.
(This is from the OS X man page, but the reasons on other systems are similar.)
fork can fail because you live in the real world, not some infinitely-recursive mathematical fantasy-land, and thus resources are finite. In particular, sizeof(pid_t) is finite, and this puts a hard upper bound of 256^sizeof(pid_t) on the number of times fork could possibly succeed (without any of the processes terminating). Aside from that, you also have other resources to worry about like memory.
There is not enough memory available to make the new process perhaps.
If the kernel fails to allocate memory for example, that's pretty bad and would cause fork() to fail.
Have a look at the error codes here:
http://linux.die.net/man/2/fork
Apparently it can fail (not really fail but hang infinitely) due to the following things coming together:
trying to profile some code
many threads
much memory allocation
See also:
clone() syscall infinitely restarts because of SIGPROF signals #97
Hanging in ARCH_FORK with CPUPROFILE #704
SIGPROF keeps a large task from ever completing a fork(). Bug 645528
Example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main()
{
size_t sz = 32*(size_t)(1024*1024*1024);
char *p = (char*)malloc(sz);
memset(p, 0, sz);
fork();
return 0;
}
Build:
gcc -pg tmp.c
Run:
./a.out

How to make processes alternating?

As for threads, I have mutex and conditionals so I could manipulate them easily.
However, if I create two processes by fork(), how could I make them alternating?
Or, is there any way to create a "critical section" for processes?
I intended to make a program that prints "r" and "w" alternatively, here is the code.
#include <stdio.h>
#include <stdlib.h>
int pipe_1[2];
int flag = 0;
void r();
void w();
int main() {
pipe(pipe_1);
if(fork())
r();
else
w();
}
void r() {
int count = 0;
while(1) {
printf("%d \n", flag);
if (count == 10)
exit(0);
if(flag == 0) {
puts("r");
flag = 1;
count++;
while(flag == 1)
;
}
}
}
void w() {
while(1) {
if(flag == 1) {
puts("w");
flag = 0;
while(flag == 0)
;
}
}
}
The out put is only:
0
r
Then it seems to enter a infinite loop.
What's the problem?
And what's the right way to make alternating processes?
Thanks.
This may be overwhelming, but there are TONS of primitives you could use. See here for a list.
http://beej.us/guide/bgipc/output/html/singlepage/bgipc.html
Glancing at the list, just about all of those could be used. Some are more like traditional pthread synchronization primitives, others are higher-level, but can still be used for synchronization.
For example, you could just open a TCP socket between the two and send messages when it's the other side's turn. Maybe with an incrementing number.
Something perhaps more traditional would be semaphores:
http://beej.us/guide/bgipc/output/html/singlepage/bgipc.html#semaphores
Also, this assumes a modern unix-like platform. Windows is likely very different.
It looks like you have a pipe already, so you can use that to have each side send a message to the other after it's done its print. The other side would do a blocking read, then return when the message was sent, do it's print, send a message back, and go back to a blocking read.
They are separate processes, so each has it's own flag; r changing its doesn't affect w's.
In order for two processes to communicate with each other without sharing the same address space (like threads do), they must use Inter-Process Communication means (aka IPC). Some of the IPC mechanisms are: shared memory, semaphore, pipes, sockets, message queues and more. Most of the time, IPC mechanisms are operating system specific. However, many ideas are general enough so it is possible to come up with a portable implementations, which Boost project did as part of Boost.Interprocess library. What I think you should take a look at first is Synchronization Mechanisms section. Note, however, that this is a C++ library. I am not aware of any C library that is as good as Boost.
Hope it helps. Good Luck!

Resources