From ca47a7767650da7d37b3824669d6277d846ee574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ciro=20Santilli=20=E5=85=AD=E5=9B=9B=E4=BA=8B=E4=BB=B6=20?= =?UTF-8?q?=E6=B3=95=E8=BD=AE=E5=8A=9F?= Date: Tue, 12 Nov 2019 00:00:00 +0000 Subject: [PATCH] fork and fork bomb moved in from cpp-cheat --- README.adoc | 42 ++++++++++++++- path_properties.py | 35 ++++++++++-- userland/posix/fork.c | 107 +++++++++++++++++++++++++++++++++++++ userland/posix/fork_bomb.c | 13 +++++ 4 files changed, 191 insertions(+), 6 deletions(-) create mode 100644 userland/posix/fork.c create mode 100644 userland/posix/fork_bomb.c diff --git a/README.adoc b/README.adoc index fbdf630..2973365 100644 --- a/README.adoc +++ b/README.adoc @@ -13783,6 +13783,8 @@ This first allows memory overcommit so to that the program can mmap 1GiB, 4x mor It then walks over every page and writes a value in it to ensure that it is used. +A <> is another example that can trigger the OOM killer. + Algorithm used by the OOM: https://unix.stackexchange.com/questions/153585/how-does-the-oom-killer-decide-which-process-to-kill-first ==== C multithreading @@ -13875,9 +13877,47 @@ These links provide a clear overview of what POSIX is: * link:userland/posix/count.c[] illustrates `sleep()` * link:userland/posix/count_to.c[] minor variation of link:userland/posix/count.c[] +==== fork + +POSIX' multiprocess API. Contrast with <> which are for threads. + +Example: link:userland/posix/fork.c[] + +Sample <> on Ubuntu 19.04 at 762cd8d601b7db06aa289c0fca7b40696299a868 + 1: + +.... +before fork before fork pid=13038 ppid=4805 +after fork after fork pid=13038 ppid=4805 +after (pid == 0) after (pid == 0) pid=13038 ppid=4805 +after fork after fork pid=13039 ppid=13038 +inside (pid == 0) inside (pid == 0) pid=13039 ppid=13038 +after wait after wait pid=13038 ppid=4805 +fork() return = 13039 +.... + +Read the source comments and understand everything that is going on! + +===== Fork bomb + +https://en.wikipedia.org/wiki/Fork_bomb + +DANGER! Only run this on your host if you have saved all data you care about! Better run it inside an emulator! QEMU v4.0.0 <> is not safe enough either because it is very native does not limit guest memory, so it will still blow up the host! + +So without further ado, let's rock: + +.... +./run --eval-after './posix/fork_bomb.out danger' +.... + +Source: link:userland/posix/fork_bomb.c[] + +Outcome on LKMC 762cd8d601b7db06aa289c0fca7b40696299a868 + 1: after a few seconds of an unresponsive shell, we get a visit form the <>, and the system is restored! + ==== pthreads -POSIX' multithreading API. This was for a looong time the only "portable" multithreading alternative, until <>, thus also extending the portability to Windows. +POSIX' multithreading API. Contrast with <> which is for processes. + +This was for a looong time the only "portable" multithreading alternative, until <>, thus also extending the portability to Windows. * link:userland/posix/pthread_count.c[] * link:userland/posix/pthread_deadlock.c[] diff --git a/path_properties.py b/path_properties.py index 896a7f7..a7cd60e 100644 --- a/path_properties.py +++ b/path_properties.py @@ -127,6 +127,27 @@ class PathProperties: }, } + # TODO wire up. + unimplemented_userland_syscalls = { + 'gem5': { + 'all': { + 'wait', + }, + 'arm': { + }, + 'x86_64': { + }, + }, + 'qemu': { + 'all': { + }, + 'arm': { + }, + 'x86_64': { + }, + }, + } + ''' Encodes properties of userland and baremetal paths. For directories, it applies to all files under the directory. @@ -620,6 +641,14 @@ path_properties_tuples = ( 'baremetal': True, 'signal_received': signal.Signals.SIGHUP, }, + 'fork.c': { + # wait + 'gem5_unimplemented_syscall': True + }, + 'mmap_file.c': { + # https://github.com/cirosantilli/linux-kernel-module-cheat/issues/102 + 'gem5_unimplemented_syscall': True + }, 'pthread_count.c': { 'more_than_1s': True, 'test_run_args': {'cpus': 2}, @@ -627,12 +656,8 @@ path_properties_tuples = ( 'pthread_self.c': { 'test_run_args': {'cpus': 2}, }, - 'mmap_file.c': { - # https://github.com/cirosantilli/linux-kernel-module-cheat/issues/102 - 'gem5_unimplemented_syscall': True - }, - 'wget.c': {'requires_internet': True}, 'sleep_forever.c': {'more_than_1s': True}, + 'wget.c': {'requires_internet': True}, 'virt_to_phys_test.c': {'more_than_1s': True}, } ), diff --git a/userland/posix/fork.c b/userland/posix/fork.c new file mode 100644 index 0000000..e22d9ea --- /dev/null +++ b/userland/posix/fork.c @@ -0,0 +1,107 @@ +/* https://cirosantilli.com/linux-kernel-module-cheat#fork */ + +#define _XOPEN_SOURCE 700 +#include +#include +#include +#include +#include +#include +#include +#include /* fork */ + +void print_pid(char *msg) { + printf("%s ", msg); + printf( + "%s pid=%jd ppid=%jd\n", + msg, + (intmax_t)getpid(), + (intmax_t)getppid() + ); +} + +int main(void) { + int status; + /* This variable will be duplicated on the parent and on the child. */ + int i; + pid_t pid; + /* Parent PID */ + pid_t ppid; + + i = 0; + ppid = getpid(); + if (ppid == -1) { + perror("getpid"); + exit(EXIT_FAILURE); + } + + /* Happens on parent only: child does not exist yet! */ + print_pid("before fork"); + /* Flush before fork so that existing output won't be duplicated. */ + fflush(stdout); + fflush(stderr); + + /* In case of success, PID is set differently on parent and child + * so you can distinguish between them. For the child, `pid = 0`. */ + pid = fork(); + if (pid == -1) { + perror("fork"); + assert(false); + } + + /* Happens both on parent and child. */ + print_pid("after fork"); + + if (pid == 0) { + /* Happens on child only. + * + * This print is asynchronous with the process stdout. + * So it might not be in the line program order. + * But they both go to the same terminal. */ + print_pid("inside (pid == 0)"); + + /* Child has a different PID than its parent */ + pid = getpid(); + if (pid == -1) { + perror("getpid"); + exit(EXIT_FAILURE); + } + assert(pid != ppid); + + /* This only change the child's `i` because memory was cloned (unlike threads). */ + i++; + + /* The child exits here. */ + exit(EXIT_SUCCESS); + } + + /* Only the parent reaches this point because of the exit call + * done on the child. + * + * Could happen before or after the child executes. */ + print_pid("after (pid == 0)"); + + /* Wait for any child to terminate, then wake up. + * Since we only have on child here, wait for that one child to terminate. */ + wait(&status); + if (WIFEXITED(status)) { + assert(status == WEXITSTATUS(EXIT_SUCCESS)); + } else { + perror("execl abnormal exit"); + assert(false); + } + + /* fork returns the child pid to the parent. + * + * This could be asserted with the getpid in the child, + * but would require the child to communicate that back to the parent, + * which would need a `mmap` + `semaphore`, + * and we don't want to complicate the example too much. */ + print_pid("after wait"); + printf("fork() return = %jd\n", (intmax_t)pid); + + /* Memory was cloned, parent `i` was only modified in child memory. */ + assert(i == 0); + + return EXIT_SUCCESS; +} diff --git a/userland/posix/fork_bomb.c b/userland/posix/fork_bomb.c new file mode 100644 index 0000000..43a6fa4 --- /dev/null +++ b/userland/posix/fork_bomb.c @@ -0,0 +1,13 @@ +/* https://cirosantilli.com/linux-kernel-module-cheat#fork-bomb */ + +#define _XOPEN_SOURCE 700 +#include +#include + +int main(int argc, char **argv) { + if (argc > 1 && strcmp(argv[1], "danger") == 0) { + while (1) { + fork(); + } + } +}