Home Knowledge Base Symbolic execution

Symbolic execution is a program analysis technique that executes programs with symbolic inputs rather than concrete values — exploring multiple execution paths simultaneously by representing inputs as symbols and tracking constraints on those symbols, enabling systematic path exploration and automated test generation.

What Is Symbolic Execution?

<svg viewBox="0 0 760 470" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,Segoe UI,Roboto,sans-serif">
  <rect x="0" y="0" width="760" height="470" fill="#0d1117"/>
  <text x="380" y="28" fill="#e6edf3" font-size="21" font-weight="700" text-anchor="middle">Symbolic Execution — Exploring All Paths</text>
  <text x="380" y="48" fill="#8b98a5" font-size="12" text-anchor="middle">execute with symbolic inputs, fork at branches, solve constraints to find bugs or prove correctness</text>

  <!-- Execution tree -->
  <rect x="30" y="65" width="430" height="310" rx="6" fill="#080d14" stroke="#233043" stroke-width="1"/>
  <text x="245" y="84" fill="#e6edf3" font-size="10" font-weight="600" text-anchor="middle">Symbolic Execution Tree</text>

  <!-- Code snippet -->
  <rect x="50" y="95" width="170" height="85" rx="4" fill="#0d1117" stroke="#334155" stroke-width="0.6"/>
  <text x="60" y="111" fill="#a78bfa" font-size="8" font-family="monospace">int f(int x, int y) {</text>
  <text x="60" y="125" fill="#8b98a5" font-size="8" font-family="monospace">  if (x &gt; 0)</text>
  <text x="60" y="139" fill="#8b98a5" font-size="8" font-family="monospace">    if (y == x+1)</text>
  <text x="60" y="153" fill="#f87171" font-size="8" font-family="monospace">      BUG(); // crash</text>
  <text x="60" y="167" fill="#8b98a5" font-size="8" font-family="monospace">  return 0; }</text>

  <!-- Execution tree (right side of left panel) -->
  <!-- Root node -->
  <circle cx="340" cy="110" r="14" fill="#0b1220" stroke="#60a5fa" stroke-width="1.2"/>
  <text x="340" y="114" fill="#93c5fd" font-size="7" text-anchor="middle">start</text>
  <text x="340" y="98" fill="#6b7684" font-size="6" text-anchor="middle">x=α, y=β</text>

  <!-- Branch: x > 0 -->
  <line x1="328" y1="122" x2="290" y2="160" stroke="#34d399" stroke-width="1"/>
  <line x1="352" y1="122" x2="390" y2="160" stroke="#f87171" stroke-width="1"/>

  <!-- Left: x > 0 TRUE -->
  <circle cx="290" cy="172" r="14" fill="#0b1220" stroke="#34d399" stroke-width="1"/>
  <text x="290" y="176" fill="#6ee7b7" font-size="7" text-anchor="middle">α&gt;0</text>
  <text x="262" y="150" fill="#34d399" font-size="6">T</text>

  <!-- Right: x <= 0 -->
  <circle cx="390" cy="172" r="14" fill="#0b1220" stroke="#6b7684" stroke-width="0.8"/>
  <text x="390" y="176" fill="#6b7684" font-size="7" text-anchor="middle">α≤0</text>
  <text x="408" y="150" fill="#f87171" font-size="6">F</text>
  <text x="390" y="196" fill="#6b7684" font-size="6" text-anchor="middle">return 0 ✓</text>

  <!-- From x>0: branch y == x+1 -->
  <line x1="278" y1="184" x2="240" y2="225" stroke="#34d399" stroke-width="1"/>
  <line x1="302" y1="184" x2="340" y2="225" stroke="#f87171" stroke-width="1"/>

  <!-- Left: y == x+1 TRUE → BUG -->
  <circle cx="240" cy="237" r="14" fill="#1a0a0a" stroke="#f87171" stroke-width="1.5"/>
  <text x="240" y="241" fill="#fca5a5" font-size="7" text-anchor="middle">BUG!</text>
  <text x="210" y="215" fill="#34d399" font-size="6">T: β=α+1</text>

  <!-- Right: y != x+1 -->
  <circle cx="340" cy="237" r="14" fill="#0b1220" stroke="#6b7684" stroke-width="0.8"/>
  <text x="340" y="241" fill="#6b7684" font-size="7" text-anchor="middle">safe</text>
  <text x="360" y="215" fill="#f87171" font-size="6">F: β≠α+1</text>

  <!-- Constraint solving -->
  <rect x="55" y="270" width="390" height="90" rx="4" fill="#0d1117" stroke="#334155" stroke-width="0.6"/>
  <text x="250" y="288" fill="#f59e0b" font-size="9" font-weight="600" text-anchor="middle">SMT Solver (Z3) finds concrete inputs:</text>
  <text x="75" y="310" fill="#8b98a5" font-size="8">Path constraint: α &gt; 0 ∧ β = α + 1</text>
  <text x="75" y="328" fill="#34d399" font-size="8">Solution: x = 1, y = 2 → triggers BUG!</text>
  <text x="75" y="346" fill="#6b7684" font-size="7.5">test case generated automatically — proves bug exists</text>

  <!-- Right panel -->
  <rect x="480" y="65" width="250" height="160" rx="6" fill="#080d14" stroke="#233043" stroke-width="1"/>
  <text x="605" y="84" fill="#e6edf3" font-size="10" font-weight="600" text-anchor="middle">How It Works</text>

  <text x="500" y="108" fill="#60a5fa" font-size="8.5" font-weight="600">1. Replace inputs with symbols</text>
  <text x="500" y="124" fill="#6b7684" font-size="7.5">x → α, y → β (unconstrained)</text>

  <text x="500" y="146" fill="#34d399" font-size="8.5" font-weight="600">2. Fork at each branch</text>
  <text x="500" y="162" fill="#6b7684" font-size="7.5">true path: add constraint to path condition</text>

  <text x="500" y="184" fill="#f59e0b" font-size="8.5" font-weight="600">3. Solve constraints at target</text>
  <text x="500" y="200" fill="#6b7684" font-size="7.5">SMT solver → concrete crashing input</text>

  <text x="500" y="220" fill="#a78bfa" font-size="8.5" font-weight="600">4. Generate test / prove safe</text>

  <!-- Tools and challenges -->
  <rect x="480" y="235" width="250" height="140" rx="6" fill="#080d14" stroke="#233043" stroke-width="1"/>
  <text x="605" y="255" fill="#e6edf3" font-size="10" font-weight="600" text-anchor="middle">Tools &amp; Limitations</text>

  <text x="500" y="278" fill="#8b98a5" font-size="8.5" font-weight="600">Tools:</text>
  <text x="500" y="294" fill="#6b7684" font-size="8">KLEE, angr, Manticore, S2E, Triton</text>

  <text x="500" y="316" fill="#f87171" font-size="8.5" font-weight="600">Challenges:</text>
  <text x="500" y="332" fill="#6b7684" font-size="8">• Path explosion (2ⁿ branches)</text>
  <text x="500" y="348" fill="#6b7684" font-size="8">• Solver timeouts (complex constraints)</text>
  <text x="500" y="364" fill="#6b7684" font-size="8">• Environment modeling (syscalls, I/O)</text>

  <text x="380" y="400" fill="#8b98a5" font-size="8.5" text-anchor="middle">concolic execution (concrete + symbolic) mitigates explosion: run concrete, collect constraints, negate one</text>

  <text x="380" y="452" fill="#6b7684" font-size="11" text-anchor="middle">Symbolic execution automatically finds inputs that crash programs — it's like fuzzing with a brain.</text>
</svg>

How Symbolic Execution Works

1. Initialize: Start with symbolic inputs (α, β, γ, ...).

2. Execute Symbolically: Interpret program operations symbolically.

3. Branch Handling: At conditional branches, fork execution.

4. Constraint Collection: Accumulate path constraints.

5. Constraint Solving: Use SMT solver to check satisfiability.

6. Test Generation: For each feasible path, generate concrete test input.

Example: Symbolic Execution

def test_function(x, y):
    z = x + y
    if z > 10:
        if x > 5:
            return "A"  # Path 1
        else:
            return "B"  # Path 2
    else:
        return "C"  # Path 3

# Symbolic execution with inputs x=α, y=β:

# Path 1: z > 10 AND x > 5
# Constraints: α + β > 10 AND α > 5
# Solver finds: α=6, β=5 → test_function(6, 5) = "A"

# Path 2: z > 10 AND x <= 5
# Constraints: α + β > 10 AND α <= 5
# Solver finds: α=5, β=6 → test_function(5, 6) = "B"

# Path 3: z <= 10
# Constraints: α + β <= 10
# Solver finds: α=3, β=2 → test_function(3, 2) = "C"

# Result: 3 test cases covering all paths!

Applications

Symbolic Execution Tools

Challenges

Optimization Techniques

Concolic Execution (Concrete + Symbolic)

1. Execute program concretely with random input. 2. Collect path constraints symbolically during execution. 3. Negate one constraint to explore alternative path. 4. Solve constraints to generate new input. 5. Repeat with new input.

Example: Finding Buffer Overflow

void vulnerable(char *input) {
    char buffer[10];
    if (strlen(input) > 10) {
        return;  // Safe path
    }
    strcpy(buffer, input);  // Potential overflow
}

// Symbolic execution:
// Input: input = symbolic string α
// Path 1: strlen(α) > 10 → return (safe)
// Path 2: strlen(α) <= 10 → strcpy(buffer, α)
//   - If strlen(α) == 10, strcpy writes 11 bytes (including null)
//   - Buffer overflow detected!
// Generated test: input = "0123456789" (10 chars)
// Triggers overflow!

LLMs and Symbolic Execution

Benefits

Limitations

Symbolic execution is a powerful program analysis technique — it systematically explores program paths to generate tests, find bugs, and verify properties, providing deeper analysis than random testing but with scalability challenges that require careful engineering.

symbolic executionsoftware engineering

Explore 500+ Semiconductor & AI Topics

From EUV lithography to CUDA optimization — search the full knowledge base or chat with our AI assistant.