I am writing my own tester for my current project. One feature (which is probably quite common with test runners) is that each test file is executed in a child process, so the tester can correctly detect and report a test failure.
I also want to test the test leader itself, so one test test should crash. I know that a βcrashβ does not fall under the C standard and can occur as a result of undefined behavior. Thus, this issue is more related to the behavior of real-world realizations.
My first attempt was to simply dereference the null pointer:
int c = *((int *)0);
This worked in a debug build on GNU / Linux and Windows, but the release build failed because the unused variable c was optimized, so I added
printf("%d", c);
and thought I was settled. However, trying my code with clang instead of gcc showed a surprise at compile time:
[CC] obj/x86_64-pc-linux-gnu/release/src/test/test/test_s.o src/test/test/test.c:34:13: warning: indirection of non-volatile null pointer will be deleted, not trap [-Wnull-dereference] int c = *((int *)0); ^~~~~~~~~~~ src/test/test/test.c:34:13: note: consider using __builtin_trap() or qualifying pointer with 'volatile' 1 warning generated.
Indeed, the clang compiled test file did not work.
So, I followed the warning tip, and now my test file looks like this:
PT_TESTMETHOD(test_expected_crash) { PT_Test_expectCrash();
This solved my immediate problem, the test file "works" (aka crashes) with both gcc and clang .
I assume that dereferencing a null pointer has undefined behavior, clang can compile my first code into something that won't work. The volatile qualifier removes the ability to be sure at compile time that this will actually result in dereferencing a null value.
Now my questions are:
- Does this final code provide zero dereferencing at runtime?
- Is dereferencing zero really a pretty portable way to crash on most platforms?