FForge
← Labsdebug-09Runtime debugging●●○○

Use of uninitialized value

debug/09_uninit_value.c3 steps
0/3 steps
evidence to leave with
valgrind conditional-jump-depends-on-uninitialised with origin
walk it, one step at a time
run this
$ clang -g -O0 09_uninit_value.c -o bin/09_uninit_value
$ ./bin/09_uninit_value ; echo "exit=$?"
what it printed on the machine that wrote this lab
exit=0
^ Here x happened to be 0 (zeroed stack page): nothing printed, exit 0.
  On a dirtied stack it may print "x is big" and exit 1. Silent UB.

gdb runs it to a clean exit — there is no signal to catch, and `print x` just shows you *a* value (gdb won't tell you it's uninitialized). For "is this value defined?", the tool is valgrind/memcheck (or clang MSan).

run this
$ valgrind -q --track-origins=yes ./bin/09_uninit_value
what it printed on the machine that wrote this lab
  ==61846== Conditional jump or move depends on uninitialised value(s)
  ==61846==    at 0x400483: main (09_uninit_value.c:68)
  ==61846==  Uninitialised value was created by a stack allocation
  ==61846==    at 0x400470: main (09_uninit_value.c:66)
  ==61846== Syscall param exit_group(status) contains uninitialised byte(s)
  ==61846==    at 0x4934048: _Exit (_exit.c:30)
  ...
  How to read it:
    "Conditional jump ... depends on uninitialised value(s)" -> the `if`
       branched on a value memcheck never saw written. The address (line
       68) is WHERE it was used.
    "Uninitialised value was created by a stack allocation ... (line 66)"
       -> --track-origins traced it to x's stack slot. memcheck pins stack
       allocations to the function entry (line 66, where x's frame is set
       up) rather than the exact `int x;` line. Without --track-origins you
       only learn THAT it's tainted, not where it was born.
    "Syscall param exit_group(status) contains uninitialised byte(s)" -> the
       `return x & 1` fed undefined bits to the exit syscall: a second,
       independent symptom of the same root cause.

ROOT CAUSE vs CRASH SITE: No crash. The USE sites are line 68 (the branch) and the return; the ROOT CAUSE is that `int x;` is never initialized (its frame slot, which memcheck reports at line 66, holds garbage). --track-origins gives you that backward link — birthplace, not just use.

category

UB / uninitialized read

difficulty

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

what's wrong

`x` is declared but never assigned, then used in `if (x>42)` and `return x & 1`. Reading an uninitialized automatic variable is UB. Buggy line: `if (x > 42)` (and `return x&1`).

symptom

NONDETERMINISTIC and usually SILENT. `x` is whatever garbage is on the stack, so whether "x is big" prints — and the exit code — can change run to run. No crash. (Compare bug #02, which was an uninitialized POINTER; this is the scalar case.)

the fix

Initialize at declaration: `int x = 0;` (or to a meaningful value).

take it further

Build with `-fsanitize=memory` (clang MemorySanitizer) if your toolchain ships it: MSan is purpose-built for uninitialized reads and is far faster than valgrind. Also try `clang -O0 -Wall -Wuninitialized` — sometimes the compiler warns at compile time (depends on how the value flows). ============================================================

read the source · debug/09_uninit_value.c
#include <stdio.h> int main(void) { int x; /* ROOT CAUSE: never initialized */ if (x > 42) /* <-- THE BUG: branch on uninitialized x */ printf("x is big\n"); return x & 1; /* also feeds undefined bits to exit() */ }
◇
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.