ADR-022: Binary Delta Signatures for Backport Detection

Formerly docs/technical/adr/0044-binary-delta-signatures.md (legacy ADR 0044).

FieldValue
StatusAccepted
Date2026-01-03
SprintSPRINT_20260102_001_BE
RelatedBinary Index semantic diffing

For: engineers on the Stella Ops scanner and Binary Index pipeline, and reviewers evaluating how Stella Ops suppresses false positives for backported security fixes. This ADR records the decision to detect patch status by comparing normalized binary code — not version strings — and to emit the result as cryptographically reproducible VEX evidence.

Context

Most vulnerability scanners rely on version-string comparison to decide whether a package is vulnerable. Linux distributions (RHEL, Debian, Ubuntu, SUSE, Alpine), however, routinely backport security fixes into older versions without bumping the upstream version number.

The Problem

Example: OpenSSL 1.0.1e on RHEL 6 has Heartbleed (CVE-2014-0160) patched, but upstream says 1.0.1e < 1.0.1g (the fix version), so scanners flag it as vulnerable. This creates:

  1. False positives - Patched systems flagged as vulnerable
  2. Alert fatigue - Security teams waste time investigating non-issues
  3. Compliance failures - Audit reports show phantom vulnerabilities
  4. Trust erosion - Users distrust scanner results

Current Mitigations

  1. Distro-specific advisory feeds (DSA, RHSA, USN) - Incomplete coverage
  2. VEX statements from vendors - Requires vendor participation, often delayed
  3. Manual triage - Doesn’t scale
  4. OVAL feeds - OS packages only, not application binaries

Requirements

Decision

Implement binary delta signature matching using normalized code comparison.

Architecture

┌────────────────────────────────────────────────────────────────────────────┐
│                        Delta Signature Pipeline                             │
├────────────────────────────────────────────────────────────────────────────┤
│                                                                            │
│  Binary         Disassembly        Normalization       Signature           │
│  ───────────►   ───────────────►   ──────────────►     ─────────────►      │
│  ELF/PE/MachO   Iced (x86) or     Zero addresses      SHA-256 +            │
│                 B2R2 (ARM/MIPS)   Canonicalize NOPs   CFG hash +           │
│                                   Normalize PLT/GOT   Chunk hashes         │
│                                                                            │
└────────────────────────────────────────────────────────────────────────────┘

Disassembly Engine Selection

Chosen: Plugin-based architecture with Iced (primary for disassembly) + B2R2 (primary for IR lifting)

EngineStrengthsWeaknessesUse Case
IcedFastest x86/x86-64, MIT license, pure C#x86 onlyFast disassembly for delta-sig normalization
B2R2Multi-arch (ARM, MIPS, RISC-V), IR lifting, MIT licenseF# (requires wrapper)Semantic IR analysis, multi-arch

Rationale:

Update (2026-01-19): B2R2 is now the primary backend for semantic IR lifting via B2R2LowUirLiftingService. This enables high-fidelity semantic analysis across x86, ARM64, MIPS, RISC-V, PowerPC, and SPARC architectures. See Binary Index semantic diffing for details.

Normalization Strategy

To compare binaries compiled by different toolchains/versions, we normalize:

  1. Zero absolute addresses - Remove PC-relative and RIP-relative variance
  2. Canonicalize NOPs - Collapse multi-byte NOPs (0x90, 0x0F1F, etc.) to single NOP
  3. Normalize PLT/GOT - Replace dynamic linking stubs with symbolic tokens
  4. Zero relocations - Remove relocation target variance
  5. Normalize jump tables - Convert absolute offsets to relative

Recipe versioning: Every signature includes the normalization recipe ID and version. Changing normalization behavior requires a version bump.

Signature Components

{
  "schema": "stellaops.deltasig.v1",
  "cve": "CVE-2014-0160",
  "package": { "name": "openssl", "soname": "libssl.so.1.0.0" },
  "target": { "arch": "x86_64", "abi": "gnu" },
  "normalization": { "recipeId": "stellaops.normalize.x64.v1", "version": "1.0.0" },
  "signatureState": "patched",
  "symbols": [
    {
      "name": "tls1_process_heartbeat",
      "hashAlg": "sha256",
      "hashHex": "abc123...",
      "sizeBytes": 1234,
      "cfgBbCount": 15,
      "cfgEdgeHash": "def456...",
      "chunks": [
        { "offset": 0, "size": 2048, "hashHex": "..." },
        { "offset": 2048, "size": 2048, "hashHex": "..." }
      ]
    }
  ]
}

Matching Strategy

  1. Exact match - Full normalized hash matches patched or vulnerable signature
  2. Chunk match - ≥70% of chunks match (handles LTO modifications)
  3. CFG match - Control flow graph structure matches (catches recompilations)

VEX Evidence Emission

When a binary is confirmed patched via delta signature:

{
  "result": "patched",
  "cveIds": ["CVE-2014-0160"],
  "confidence": 0.95,
  "symbolMatches": [
    { "symbolName": "tls1_process_heartbeat", "state": "patched", "exactMatch": true }
  ],
  "justification": "vulnerable_code_not_present",
  "summary": "Binary confirmed PATCHED with 95% confidence. 1 symbol(s) matched patched signatures exactly."
}

This evidence feeds into VEX candidate generation with full audit trail.

Alternatives Considered

1. Source Code Comparison

Rejected: Requires source access, doesn’t work for closed-source binaries, compile options affect behavior.

2. Debug Symbol Matching

Rejected: Symbols often stripped in production, doesn’t prove code content.

3. File Hash Matching

Rejected: Entire binary must match exactly; any rebuild invalidates signature.

4. YARA Rules

Rejected: Pattern-based, high false positive rate, doesn’t provide cryptographic proof.

5. Single Disassembly Engine (B2R2 only)

Rejected: Performance critical; Iced is 3-5x faster for x86/x86-64 which is 90%+ of scanned binaries.

Consequences

Positive

  1. Eliminate false positives for backported security fixes
  2. Cryptographic proof of patch status (auditable, reproducible)
  3. Offline operation with signature packs
  4. Multi-architecture support for modern infrastructure
  5. VEX integration for automated triage

Negative

  1. Signature authoring required - Must create signatures for each CVE/package
  2. Normalization limits - Extreme compiler optimizations may defeat matching
  3. Storage overhead - Signature database growth
  4. Compute cost - Disassembly + normalization per binary

Mitigations

Implementation

Sprint: SPRINT_20260102_001_BE

ComponentStatusNotes
Disassembly.AbstractionsDONEPlugin interface, models
Disassembly.IcedDONEx86/x86-64 support
Disassembly.B2R2DONEMulti-arch support
NormalizationDONEX64 + ARM64 pipelines
DeltaSigDONEGenerator + matcher
PersistenceDONEPostgreSQL schema
CLIDONEextract, author, sign, verify, match, pack, inspect
Scanner integrationDONEDeltaSigAnalyzer, IBinaryVulnerabilityService
VEX emissionDONEDeltaSignatureEvidence, DeltaSigVexEmitter

Test Coverage

References