We use essential cookies to run the site (session, security, and your theme/language preferences). With your permission we also load embedded third-party content, such as YouTube videos. Cookie Policy
Every program bug has at least one concrete input that triggers it. The tricky part is finding that input without knowing it in advance.
Symbolic execution flips the question. Instead of picking a specific number and running the program, it leaves the input as an unknown — call it x — and asks: what does the program's behavior look like for all possible values of x at once?
As the program executes, each branch it takes adds a constraint on x. After a if (x > 10) branch, you know x>10. After another if (x == 42) branch, you know x=42. When a crash path is reached, the accumulated constraints form a system of equations. Hand that system to a constraint solver (typically a SAT or SMT solver), and it hands back a concrete input that walks straight into the crash.
Invented in 1976 by James C. King, symbolic execution stayed largely theoretical for decades — constraint solvers were too slow. Once modern SMT solvers appeared in the mid-2000s, tools like DART, SAGE, and KLEE turned it into a practical weapon for finding security vulnerabilities in real software.
Solve for the Bug
The demo below simulates symbolic execution on a tiny program with three branches and a hidden crash. The input x starts as a symbolic unknown. Click Step to advance one branch at a time and watch the path constraint grow. When the engine reaches the crash, click Solve to have the constraint solver find a concrete crashing value.
<!-- Symbolic execution demo — step through branches, collect constraints, solve for the crash -->
<p class="hint">The program below has a hidden crash. Click <b>Step</b> to explore one branch at a time — each branch adds a constraint on the unknown input <i>x</i>. Once you reach the crash, click <b>Solve</b> to let the constraint solver find the exact crashing value.</p>
<div class="program-box">
<pre id="program-code"></pre>
</div>
<div class="engine-panel">
<div class="panel-label">Path constraints</div>
<div id="constraint-list" class="constraint-list"></div>
</div>
<div id="status-msg" class="status-msg"></div>
<div class="btns">
<button id="btn-step" type="button">Step</button>
<button id="btn-solve" type="button" disabled>Solve</button>
<button id="btn-reset" type="button" class="ghost">Reset</button>
</div>
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// The simulated program: check(x) crashes iff x > 10 AND x < 100 AND x === 42.
// Unique solution: x = 42 satisfies all three constraints.
const PROGRAM_LINES = [
{ code: '<span class="kw">function</span> <span class="fn">check</span>(x) {', type: 'header' },
{ code: ' <span class="kw">if</span> (x > <span class="num">10</span>) {', type: 'branch', constraint: 'x > 10', side: true },
{ code: ' <span class="kw">if</span> (x < <span class="num">100</span>) {', type: 'branch', constraint: 'x < 100', side: true },
{ code: ' <span class="kw">if</span> (x === <span class="num">42</span>) {', type: 'branch', constraint: 'x === 42', side: true },
{ code: ' <span class="kw">throw</span> <span class="num">\'crash!\'</span>; <span class="cmt">// BUG: crash here!</span>', type: 'crash' },
{ code: ' } <span class="kw">else</span> { <span class="cmt">// safe path</span>', type: 'safe' },
{ code: ' }', type: 'close' },
{ code: ' }', type: 'close' },
{ code: ' }', type: 'close' },
{ code: '}', type: 'footer' }
];
// Steps: each step activates one branch and adds a constraint to the path.
const STEPS = [
{ lineIdx: 1, constraint: 'x > 10', msg: 'step1_msg' },
{ lineIdx: 2, constraint: 'x < 100', msg: 'step2_msg' },
{ lineIdx: 3, constraint: 'x = 42', msg: 'step3_msg' },
{ lineIdx: 4, constraint: null, msg: 'step4_msg', isCrash: true }
];
let stepIdx = 0;
const constraints = [];
// The unique value satisfying all constraints (x > 10 AND x < 100 AND x = 42).
const SOLUTION = 42;
function renderProgram(activeIdx, mode) {
// Render program lines, highlighting the active or crashed line
const pre = document.getElementById('program-code');
pre.innerHTML = PROGRAM_LINES.map((ln, i) => {
let cls = 'line';
if (i === activeIdx) cls += (mode === 'crash' ? ' crashed' : mode === 'ok' ? ' ok' : ' active');
return `<span class="${cls}">${ln.code}</span>`;
}).join('');
}
function renderConstraints(newKey) {
// Update the constraint chip list
const list = document.getElementById('constraint-list');
list.innerHTML = constraints.map((c, i) => {
const isNew = newKey && i === constraints.length - 1;
return `<span class="chip${isNew ? ' new' : ''}">${c}</span>`;
}).join('') || `<span style="color:#999;font-size:.85rem">(none yet — click Step)</span>`;
}
function setStatus(text, cls) {
const el = document.getElementById('status-msg');
el.textContent = text;
el.className = 'status-msg ' + cls;
}
function step() {
if (stepIdx >= STEPS.length) return;
const s = STEPS[stepIdx];
stepIdx++;
if (s.constraint) constraints.push(s.constraint);
const mode = s.isCrash ? 'crash' : 'active';
renderProgram(s.lineIdx, mode);
renderConstraints(!!s.constraint);
const msgKey = s.msg;
// Look up the user-visible message for this step
const msgs = {
step1_msg: 'Branch taken: x > 10. Constraint added.',
step2_msg: 'Branch taken: x < 100. Constraint added.',
step3_msg: 'Branch taken: x = 42. Constraint added.',
step4_msg: 'Crash reached! All three constraints must hold simultaneously. Click Solve.'
};
const msgCls = s.isCrash ? 'crash' : 'info';
setStatus(msgs[msgKey] || '', msgCls);
if (s.isCrash) {
document.getElementById('btn-step').disabled = true;
document.getElementById('btn-solve').disabled = false;
}
}
function solve() {
// Constraint solver derives the concrete crashing input from accumulated constraints
renderProgram(4, 'crash');
setStatus(i18nFmt('Solver found x = %x%. That is the only input that satisfies all constraints and triggers the crash.', { x: SOLUTION }), 'ok');
}
function reset() {
// Reset engine state for a fresh run
stepIdx = 0;
constraints.length = 0;
renderProgram(-1, '');
renderConstraints(false);
setStatus('Click Step to begin symbolic execution.', 'info');
document.getElementById('btn-step').disabled = false;
document.getElementById('btn-solve').disabled = true;
}
document.getElementById('btn-step').addEventListener('click', step);
document.getElementById('btn-solve').addEventListener('click', solve);
document.getElementById('btn-reset').addEventListener('click', reset);
reset();
<!-- Symbolic execution demo — step through branches, collect constraints, solve for the crash -->
<p class="hint">The program below has a hidden crash. Click <b>Step</b> to explore one branch at a time — each branch adds a constraint on the unknown input <i>x</i>. Once you reach the crash, click <b>Solve</b> to let the constraint solver find the exact crashing value.</p>
<div class="program-box">
<pre id="program-code"></pre>
</div>
<div class="engine-panel">
<div class="panel-label">Path constraints</div>
<div id="constraint-list" class="constraint-list"></div>
</div>
<div id="status-msg" class="status-msg"></div>
<div class="btns">
<button id="btn-step" type="button">Step</button>
<button id="btn-solve" type="button" disabled>Solve</button>
<button id="btn-reset" type="button" class="ghost">Reset</button>
</div>