FForge
← Labsdebug-13Runtime debugging●●○○

Call through a null function pointer

debug/13_null_funcptr.c3 steps
0/3 steps
evidence to leave with
gdb stops at address 0; frame 1 names the caller
walk it, one step at a time
run this
$ clang -g -O0 13_null_funcptr.c -o bin/13_null_funcptr
$ ./bin/13_null_funcptr ; echo "exit=$?"
what it printed on the machine that wrote this lab
calling through a null function pointer...
Segmentation fault (core dumped)
exit=139
run this
$ gdb -q -batch -ex 'set debuginfod enabled off' -ex run -ex bt \
what it printed on the machine that wrote this lab
      -ex 'x/i $pc' -ex 'info registers rip' -ex 'frame 1' \
      -ex 'print f' ./bin/13_null_funcptr
Program received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()
=> 0x0:	Cannot access memory at address 0x0
#0  0x0000000000000000 in ?? ()
#1  0x000000000040049b in main () at 13_null_funcptr.c:83
rip            0x0                 0x0
#1  0x000000000040049b in main () at 13_null_funcptr.c:83
83      f();                  // <-- THE BUG
$1 = (fn_t) 0x0
How to read each command:
  bt           -> frame #0 is "0x0 in ?? ()": there is NO function at
                  address 0, so gdb has no symbol — that "??" is the tell
                  that you jumped through a bad pointer. Frame #1 is YOUR
                  code: main at line 83, the call site.
  x/i $pc      -> "Cannot access memory at address 0x0": you can't even
                  disassemble the current instruction — $pc points at
                  unmapped memory. Proof the fault is a bad code fetch.
  info reg rip -> rip == 0x0: the program counter itself is null.
  frame 1      -> SWITCH to main's frame. You must do this before
                  inspecting main's locals: in frame #0 (??) there is no
                  debug info, so `print f` fails with "No symbol f in
                  current context". After `frame 1` it works:
  print f      -> $1 = (fn_t) 0x0  — the null function pointer, confirmed.
run this
$ clang -g -O0 -fsanitize=address 13_null_funcptr.c -o bin/13_null_funcptr.asan
$ ./bin/13_null_funcptr.asan
what it printed on the machine that wrote this lab
  AddressSanitizer:DEADLYSIGNAL
  ==NNNNN==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000
  ==NNNNN==The signal is caused by a READ memory access.  (the instruction fetch)
  ASan confirms a SEGV at address 0; gdb is what tells you it was a CALL
  (rip=0, frame #0 = "??") rather than a data access. valgrind is not needed
  here (it's a plain SIGSEGV, no heap involved).

ROOT CAUSE vs CRASH SITE:
  The crash SITE is address 0 (frame #0, "??") — not in your source at all.
  The ROOT CAUSE is the call `f()` with f==NULL (frame #1, line 83). With
  indirect calls, ALWAYS read frame #1: frame #0 is just "wherever the bad
  pointer sent us". A null/garbage `rip` + a "??" frame #0 == jumped through
  a bad function pointer.
category

segfault (bad control transfer)

difficulty

2 ------------------------------------------------------------

what's wrong

`f` is a function pointer set to NULL, then CALLED: `f();`. The CPU tries to jump to address 0 and execute — but address 0 is unmapped and not executable, so it faults. Buggy line: `f();`. (Unlike a data NULL deref, the fault is at the INSTRUCTION FETCH: $pc itself becomes 0.)

symptom

"Segmentation fault (core dumped)", exit 139. Deterministic.

the fix

Never call an unverified function pointer: if (f) f(); // guard it and initialize callbacks to a real function (or a safe default), not NULL.

take it further

Set `f = (fn_t)0xdeadbeef;` instead. Re-run under gdb: now frame #0 is "0xdeadbeef in ?? ()" and rip==0xdeadbeef — same shape, different bad address. The pattern (??/garbage rip) is how you recognize the bug class. ============================================================

read the source · debug/13_null_funcptr.c
#include <stdio.h> typedef void (*fn_t)(void); int main(void) { fn_t f = NULL; /* null function pointer */ printf("calling through a null function pointer...\n"); f(); /* <-- THE BUG: call via NULL */ return 0; }
◇
Finish the lab: write the evidence note

A lab is done when you can say what it showed on your machine — not when you have read it.