When a CLI process in Linux exits after a segmentation fault, the following message is typically printed to stdout: “Segmentation fault (core dumped)”. We are assuming here that the process did not register a handler for the SIGSEGV signal. I was a bit curious about who was printing the message so started to dig a bit.
My first hypothesis was that libc had a default handler for this signal. After running the application with strace, I found no sys_write system call: the application and its libraries were not printing anything.
If a process registers no handler for SIGSEGV, do_coredump function (fs/coredump.c – Linux kernel) is executed. Caught my attention that the Kernel creates a new task and launches a user mode application. This application is /usr/libexec/abrt-hook-ccpp in Fedora, and the goal is to record and report the crash. I ran strings over its binary and dynamically linked libraries (libc, libreport, libabrt, etc.) but no clues.
After some more source code grepping over potential candidates, such as systemd-coredump, my curiosity was even larger and finally opted for the hard way: monitor every single sys_write system call in the kernel.
The first place to insert a monitor code was n_tty_write function (drivers/tty/n_tty.c – Linux kernel), as every TTY write hits there. I set a breakpoint in the monitor code and it was immediately triggered. Looking at the task memory map, I realized it was bash. But then I thought: what if it’s not bash but a child process sending the message through a pipe?
A second monitor code was placed a bit higher in the call stack: __vfs_write function (fs/read_write.c – Linux kernel). A breakpoint there was hit and it was bash again. Downloaded the bash source and could verify it.
Here it’s the monitor:
C
Notes: 1) nops are there to make finding the address for the breakpoint easier, and 2) memset could probably be removed if using the proper flags when calling kmalloc.
Here it’s how it looks in assembly:
RAX register has a pointer to the current task struct.
This is how we can have a look at the task memory map and determine the file associated to the first segment:
结束!