m0nad / Diamorphine

LKM rootkit for Linux Kernels 2.6.x/3.x/4.x/5.x/6.x (x86/x86_64 and ARM64)
Other
1.79k stars 425 forks source link

Segmentation fault at readdir64 on CentOS 7 #31

Open vikman90 opened 3 years ago

vikman90 commented 3 years ago

Hi, we've found an issue that happens during a scan of /proc on CentOS 7 (kernel 3.10.0-1160.36.2.el7.x86_64): the program sometimes crashes (https://github.com/wazuh/wazuh/issues/9737) and sometimes falls into an infinite loop (https://github.com/wazuh/wazuh/issues/9032), when Diamorphine (commit 8988105) is installed.

We've written this program to detect this problem:

testproc.c ```c #include #include #include #include #include #include #include #include volatile bool keepalive = false; // List content of a directory static void list_dir(const char * dirname, int depth) { char fname[PATH_MAX]; struct dirent * entry; DIR * dir = opendir(dirname); if (dir == NULL) { return; } while ((entry = readdir(dir)) != NULL) { if (entry->d_name[0] == '.' && (entry->d_name[1] == '\0' || (entry->d_name[1] == '.' && entry->d_name[2] == '\0'))) { continue; } snprintf(fname, PATH_MAX, "%s/%s", dirname, entry->d_name); puts(fname); if (depth > 0) { list_dir(fname, depth - 1); } } closedir(dir); keepalive = true; } // Monitor keepalive with 1-second timeout static void * monitor_main() { int delay = 0; while (true) { sleep(1); if (keepalive) { if (delay > 0) { fprintf(stderr, "INFO: Program is now responding.\n"); } delay = 0; keepalive = false; } else { fprintf(stderr, "WARNING: Program is taking %d sec. to respond.\n", ++delay); } } return NULL; } // Launch monitor thread static void run_monitor() { pthread_t thread; int r = pthread_create(&thread, NULL, (void *(*)(void *))monitor_main, NULL); if (r != 0) { fprintf(stderr, "ERROR: Cannot run thread: %s\n", strerror(r)); exit(EXIT_FAILURE); } r = pthread_detach(thread); if (r != 0) { fprintf(stderr, "ERROR: Cannot detach thread: %s\n", strerror(r)); } } int main(int argc, char ** argv) { run_monitor(); while (true) { if (argc > 1) { int i; for (i = 1; i < argc; i++) { list_dir(argv[i], 1); } } else { list_dir("/proc", 1); } } return EXIT_SUCCESS; } ```

How to reproduce

We've managed to reproduce this problem by scanning /proc and launching lots of ephemeral processes (/bin/echo). I think that the issue occurs when a directory (or its content) is removed while readdir() is getting it. However, I don't know if this happens only with /proc.

insmod diamorphine.ko
for i in {1..8}; do while true; do /bin/echo Hello > /dev/null; done & done

Catching the issue

gcc -g -pthread -o testproc testproc.c
./testproc > /dev/null

(... Few seconds after running the loops above ...)

WARNING: Program is taking 1 sec. to respond.
WARNING: Program is taking 2 sec. to respond.
WARNING: Program is taking 3 sec. to respond.
WARNING: Program is taking 4 sec. to respond.
(...)

Backtrace

#0  0x00007f8f83b9b334 in __getdents64 () from /lib64/libc.so.6
#1  0x00007f8f83b9af11 in readdir64 () from /lib64/libc.so.6
#2  0x00000000004009bb in list_dir (dirname=0x7ffe3a11ff10 "/proc/31244",
    depth=0) at testproc.c:22
#3  0x00000000004009af in list_dir (dirname=0x400c91 "/proc", depth=1)
    at testproc.c:31
#4  0x0000000000400b5b in main (argc=1, argv=0x7ffe3a121038)
    at testproc.c:88

Hope this helps find the cause of the bug.

Thank you.