Instead of running code on concrete numbers, run it on a symbol — a variable standing for any input. At each branch the path splits, and the checker accumulates a path condition: the constraints an input must satisfy to walk this exact route. To reach a target line — an error, an unhandled case — you hand the accumulated constraints to a solver and it produces the concrete input that gets there. It does not sample inputs; it solves for them.
A function reaches its target branch only when x>5 ∧ x<10 ∧ x even. Symbolic execution conjoins those constraints and solves for the input that reaches it — here the first witness: live demo
“To reach a deep buggy branch you have to be lucky enough to guess the input.” — symbolic execution does not guess. It reads off the exact conditions the branch requires and asks a constraint solver for a satisfying assignment. Deep bugs behind narrow guards, which fuzzing may never hit by chance, fall out as solved equations. cited
Where the fuzzer knocks blindly and hopes, symbolic execution reads the lock and cuts the key. The path condition is the shape of the input the bug demands — solve it, and you are there. King 1976
On the canonical compiler, the target path requires x>5 ∧ x<10 ∧ (x&1)==0; solving that conjunction yields the reaching input: