Where did my segfault go?

Lobsters Hottest News

Summary

The author investigates why segmentation faults are not displayed when using entr to run a C program, discovering that bash's exec optimization suppresses the error message unless the command is run in a subshell or script.

<p><a href="https://lobste.rs/s/tedtzz/where_did_my_segfault_go">Comments</a></p>
Original Article
View Cached Full Text

Cached at: 07/11/26, 09:27 PM

# Where did my segfault go? Source: [https://rmpr.xyz/Where-did-my-segfault-go/](https://rmpr.xyz/Where-did-my-segfault-go/) The other day I was iterating on a small C program with[entr](https://eradman.com/entrproject/): ``` ls hello.c | entr -s "gcc -o hello hello.c && ./hello" ``` `hello`happened to segfault, but entr showed me… nothing\. No`Segmentation fault`, no non\-zero exit visible, just complete silence after the compile output\. Hmm, very weird\. Let’s prefix with`bash \-c`: ``` ls hello.c | entr -s "bash -c 'gcc -o hello hello.c && ./hello'" ``` Still nothing\. What about moving the command into a script: ``` #!/bin/bash gcc -o hello hello.c && ./hello ``` And point entr to it: ``` ls hello.c | entr -s ./run.sh ./run.sh: line 2: 104465 Segmentation fault (core dumped) ./hello ``` There’s my segmentation fault\! ## The reason\(s\) > Thanks to[superuser](https://superuser.com/a/415954)\(remember that site we used to use pre LLMs?\) for pointing me in the right direction\. The question is not exactly the same, but it still helped me\. At first, I thought the problem was with`entr`because I could reproduce both with bash and fish\. **Who actually prints “Segmentation fault”?**Obviously not the crashing program\. It can’t since it’s dead\. The message is printed by the*shell*when it reaps a child that died from`SIGSEGV`\. No parent shell, no message, simple enough\. But wait, why does even the explicit`bash \-c 'gcc && \./hello'`version stay silent? Turns out bash likes to`exec`the command if it can\. When you run`bash \-c "some\_command"`and`some\_command`is effectively the only thing to do,[bash doesn’t bother forking a new process for it](https://cgit.git.savannah.gnu.org/cgit/bash.git/tree/execute_cmd.c?id=ecb2456d6357bfd3b2965aafe2f2021d1ced5b72#n5827)\. It just`execve`s into it, replacing itself\. It’s a nice optimization, invisible 99% of the time\. In the script version, entr runs`\./run\.sh`as a child, and the script’s shebang starts a fresh`bash`to interpret the file\. That bash runs`gcc`, forks for`\./hello`, waits, sees it died from`SIGSEGV`, and dutifully prints`Segmentation fault`before exiting\. ## The follow\-up But then it occurred to me, maybe you don’t need a wrapper script after all, what happens if I have the crashing command running in a subshell? ``` $ ls hello.c | entr -s "gcc -o hello hello.c && (./hello)" hello.c: line 1: 106595 Segmentation fault (core dumped) ( ./hello ) ``` Yep, I don’t think I have ever been that satisfied to see a program segfaulting\. Here the parens force`\./hello`into a forked subshell, so bash can’t`exec`its way out of being the parent\. When the subshell dies from`SIGSEGV`, bash reaps it and prints the message like normal\. I could have stopped there but I wanted a way to test my second hypothesis\. What if you just make sure the shell has something to do after the crashing command? ``` $ ls hello.c | entr -s "bash -c 'gcc -o hello hello.c && ./hello; true'" bash: line 1: 109516 Segmentation fault (core dumped) ./hello ``` Victory\! Now I should probably get back to writing some C…

Similar Articles

Your executable is a SQLite database

Simon Willison's Blog

A blog post explains a Linux pattern where a SQLite database file is formatted to act as an ELF executable, using a custom interpreter and binfmt_misc for direct execution.

Tips to debug hanging Go programs

Michael Stapelberg

A practical guide covering three methods to debug hanging Go programs: using SIGQUIT to print stack traces, attaching the delve debugger, and saving core dumps for later analysis.

Arbitrary code execution in objdump -g

Lobsters Hottest

A security vulnerability in objdump -g allows arbitrary code execution via a crafted FR30 object file due to a missing bounds check in the FR30 relocation handler, with a single-shot exploit that defeats ASLR and other mitigations.