§0 Birth of Vulnerabilities — Not Bugs — From First Principles
A Derivation of the Necessity of Exploitable Error from the Physical Laws of Computation
Author: Shrikant Bhosale (@debtcollector21)
Version: 1.0 (Formalized July 2026)
Epistemological Status: First-principles derivation — not empirical, not heuristic, not opinion
Preamble: The Question
Every vulnerability researcher asks: Why do vulnerabilities exist? The usual answer is: Because programmers make mistakes.
This answer is wrong.
Programmers do make mistakes. Those mistakes are bugs. But bugs have existed since the first program, and most bugs are not vulnerabilities. The question is not why programmers err — it is why some errors become exploitable while most remain harmless.
This document answers that question from first principles. We begin not with code, not with computers, but with physics. We derive the existence of vulnerabilities as a necessary consequence of the relationship between human intent, physical law, and the thermodynamics of computation.
A vulnerability is not a bug. A vulnerability is a topological singularity in the mapping between what a programmer intends and what a physical system can enforce. Bugs are accidents. Vulnerabilities are inevitable.
S1: The Physical Premise
1.1 Computation Is Physical
Every computation is a physical process. A bit is not an abstraction — it is a physical state: a capacitor charge, a magnetic domain, a phonon mode, an [Vendor] spin. The program is not a Platonic ideal — it is a sequence of physical state transitions governed by the laws of thermodynamics, electromagnetism, and quantum mechanics.
Axiom 1 (Physical Computation): There is no computation without a physical substrate. The program, the data, the state, and the output are all physical configurations of matter and energy.
This axiom is not controversial — it is the foundation of Landauer’s principle, the physics of CMOS circuits, and the theory of quantum computation. It has one consequence that is rarely stated explicitly:
Corollary 1.1 (The Irreducible Gap): The mapping from a programmer’s mental model (a formal specification) to a physical state (the executing machine) is always lossy. The programmer works in the domain of intentional semantics; the machine operates in the domain of physical dynamics. These are not the same domain.
1.2 The Three Irreducible Gaps
Let a program be a triple:
P = (I, S, M)
Where:
– I = the programmer’s intent — the informal, often incomplete, mental model of what the program should do
– S = the specification — the formal encoding of intent in a programming language
– M = the machine — the physical system executing the specification
Three gaps exist:
Gap 1: I -> S (Intent Gap)
The programmer’s intent is never fully captured in code. Intent is contextual, tacit, and infinite. Code is explicit, bounded, and finite. Every line of code is a lossy compression of what the programmer actually means.
D_IS = H(I | S) > 0 (residual entropy of intent not captured by specification)
Gap 2: S -> M (Execution Gap)
The specification is interpreted by a toolchain (compiler, interpreter, JIT) that transforms it into machine states. This transformation is not neutral — it can add, remove, or restructure information.
D_SM = H(S | M) > 0 (residual entropy of specification not preserved in machine)
Gap 3: M -> I (Verification Gap)
The programmer cannot perfectly verify that the machine state matches their intent. Verification is itself a computation, subject to the same physical limits.
D_MI = H(M | I) > 0 (residual entropy of machine state not verifiable against intent)
Theorem 0 (The Three-Gap Theorem): For any non-trivial program P, all three gaps are strictly positive:
D_IS > 0 AND D_SM > 0 AND D_MI > 0
Proof:
Gap 1 (I -> S): The programmer’s intent includes tacit knowledge — assumptions about the environment, the user, the hardware, the edge cases that “would never happen.” By the infinitude of possible inputs and the finitude of specifications (Goedel, Turing), no finite specification can capture all possible intents. Therefore D_IS > 0.
Gap 2 (S -> M): The toolchain (compiler + interpreter) is itself a program with its own Intent Gap. The compiler’s authors intend correct translation; the compiler may miscompile. By Goedel’s second incompleteness theorem, no sufficiently powerful compiler can prove its own correctness. Therefore D_SM > 0.
Gap 3 (M -> I): Verification is a halting problem. The programmer cannot, in general, determine whether a given machine state satisfies their intent (Rice’s theorem). Testing can falsify but never verify. Therefore D_MI > 0.
QED
S2: Bugs vs Vulnerabilities — The Fundamental Distinction
2.1 Definition of a Bug
Definition 1 (Bug): A bug is a discrepancy between the specification S and the machine M such that the specification’s intended behavior is violated but no attacker-controlled input can reach the violating state.
Formally:
Bug: There exists s in S, m in M such that:
1. m != delta_S(s) (machine state differs from specified transition)
2. There does NOT exist sigma in Sigma_attack
such that delta_M(m0, sigma) = m (no attacker input reaches m)
A bug is a latent error — incorrect but unreachable by adversarial inputs.
2.2 Definition of a Vulnerability
Definition 2 (Vulnerability): A vulnerability is a discrepancy between the specification S and the machine M such that an attacker-controlled input can drive the machine into a state that violates the programmer’s intent I.
Formally:
Vulnerability: There exists sigma in Sigma_attack, s in S such that:
1. delta_M(m0, sigma) = m (attacker reaches machine state m)
2. m NOT in I (machine state violates programmer intent)
3. m in E (machine state is an error state — undefined or unhandled)
2.3 The Key Distinction
Bugs: Specification != Machine (but attacker cannot reach)
Vulnerabilities: Intent != Machine (and attacker can reach)
A bug becomes a vulnerability when three conditions align:
1. An error path exists in the code (specification error)
2. An attacker can reach that error path (reachability)
3. The error path leads to a state the programmer did not intend (intent violation)
This is the Birth of a Vulnerability — the confluence of all three gaps.
S3: The Birth — A First-Principles Derivation
3.1 Starting from Physics
We begin with the Second Law of Thermodynamics.
Axiom 2 (Second Law): In any closed system, entropy tends to increase. The entropy of an isolated system never decreases.
Axiom 3 (Landauer’s Principle): Erasing one bit of information dissipates at least kT ln(2) energy. Computation is thermodynamically irreversible.
Axiom 4 (Finite Precision): Every physical state has finite precision. No measurement, no storage, and no computation can achieve infinite precision.
3.2 The Derivation
Step 1: Programs are interfaces between intent and physics.
A program is a designed physical system that maps input states to output states. The designer (programmer) has an intent — a set of acceptable input-output pairs. The physical system has dynamics — a set of possible transitions.
Let:
– I = set of all input-output pairs the programmer intends
– M = set of all input-output pairs the machine can physically produce
The programmer’s knowledge is incomplete because I is a proper subset of M:
I* subset M* (intent is a subset of what the machine can do)
Proof: The machine can produce any behavior consistent with physics. The programmer intends only a subset of those behaviors. By Axiom 1, the machine’s possible behaviors are determined by physics, not by the programmer. Therefore I != M. Since I is constructed by a finite mind and M is determined by infinite physical possibilities, I* is strictly smaller.
Step 2: Error states are the complement of intent.
Define the set of error states E as:
E = M* \ I*
E is the set of machine states the programmer does NOT intend. By Step 1, E is non-empty.
Step 3: Error handling partitions E.
The programmer writes error-handling code that maps some subset of E to acceptable states. Let H be the handling function:
H: E -> {handled, unhandled}
Define:
– E_handled = {e in E : H(e) = handled}
– E_unhandled = {e in E : H(e) = unhandled}
By Theorem 0 (Three-Gap Theorem), E_unhandled is non-empty. The programmer cannot enumerate all possible error states because the gap I -> S is irreducible.
Step 4: Information Debt.
The Information Debt is the proportion of error states that are unhandled:
D_e = |E_unhandled| / |E|
Since E_unhandled is non-empty, D_e > 0 for any non-trivial program.
Step 5: Existence of exploit path.
By the definition of the machine M, there exists at least one transition path from initial state m0 to each unhandled error state e in E_unhandled. The path may be through attacker-controlled inputs.
Definition 3 (Reachable Error): An error state e in E_unhandled is reachable if there exists an input sequence sigma_1, …, sigma_k in Sigma_attack such that:
delta_M(...delta_M(delta_M(m0, sigma_1), sigma_2)..., sigma_k) = e
Theorem 1 (Birth of Vulnerability): For any program P with D_e > 0, there exists at least one reachable unhandled error state e in E_unhandled such that e is exploitable (leads to a violation of I).
Proof:
Let P be a program with D_e > 0. Then E_unhandled is non-empty. Choose any e in E_unhandled.
Since e is an error state, e is in M \ I — the machine can physically reach e, but the programmer did not intend it.
By the definition of the machine M, there exists a sequence of inputs leading from m0 to e. The sequence may require specific input values — values the programmer did not anticipate.
The attacker’s goal is to find this sequence. By the pigeonhole principle applied to the input space Sigma_attack (which is exponentially large for any non-trivial program), the probability that the programmer has explicitly blocked ALL paths to ALL unhandled error states is zero for any finite program.
Specifically, let B be the set of all input sequences. Let K be the set of input sequences the programmer explicitly validates. Since the programmer’s specification is finite (finite lines of code) and B is infinite (or exponentially large), K is a proper subset of B. Therefore there exists at least one input sequence sigma in B \ K that reaches an unhandled error state.
This input sequence is the exploit.
QED
3.3 The Quadratic Relation (Derived from First Principles)
From Step 5, the probability that an attacker finds an exploit in one attempt is:
P(find_exploit) = |E_unhandled_and_reachable| / |Sigma_attack|
This is proportional to D_e (more debt = more unhandled states). But exploitation requires two unhandled errors composing:
1. An error that corrupts state (write primitive)
2. An error that uses the corrupted state (control primitive)
By independence of error events:
P(exploit) proportional to D_e^2
This is not an empirical observation. It is derived from the composition of two independent events in the state-transition model.
S4: Why Vulnerabilities Are Inevitable
4.1 The Inevitability Theorem
Theorem 2 (Inevitability of Vulnerabilities): For any sufficiently complex program P (one that accepts inputs of unbounded length), the existence of at least one exploitable vulnerability is a mathematical certainty.
Proof:
Let P be a program that accepts inputs of unbounded length. By Theorem 0, at least one of the three gaps (I -> S, S -> M, M -> I) is non-zero.
Since inputs are unbounded, the set of possible input sequences Sigma is countably infinite. The programmer’s specification S is finite (finite number of lines of code, finite number of explicit checks).
The validation predicates the programmer can write are finite in number. Each predicate partitions Sigma into two subsets: those that pass and those that fail. The total number of distinct subsets the programmer can distinguish is 2^N where N is the number of predicates. Since N is finite, the programmer can distinguish at most 2^N subsets of an infinite input space.
By the pigeonhole principle, there must be at least one subset of Sigma that contains both valid and invalid inputs — inputs the programmer treated identically but that have different effects on the machine. This subset is the vulnerability class.
Since the number of possible vulnerability classes grows exponentially with program complexity, and the number of explicit checks grows only linearly (or polynomially) with programmer effort, for sufficiently complex programs the number of vulnerability classes exceeds the number of checks. By the Pigeonhole Principle, at least one vulnerability must remain unhandled.
QED
4.2 Corollaries
Corollary 2.1 (No Perfect Security): No finite program with unbounded input space can be perfectly secure.
Proof: Follows directly from Theorem 2.
Corollary 2.2 (Complexity-Security Tradeoff): Security is inversely related to program complexity. As program size N increases, the minimum number of vulnerabilities grows as Omega(N).
Proof sketch: Each new feature (increase in N) adds at least one new input dimension. Each new input dimension multiplies the input space. The number of checks grows as O(N) (programmer writes one check per feature). The number of possible vulnerability classes grows as O(2^N). By Theorem 2, the gap between checks and vulnerability classes grows with N.
Corollary 2.3 (The TAC Connection): A TAC (Thermodynamic Automaton Computer) does not escape this inevitability — it shifts the vulnerability surface from the specification layer to the physical substrate layer.
Proof: A TAC replaces the software specification S with a physical cost functional F[s]. The three gaps become:
- Intent -> Cost Functional: The programmer must encode their intent as a thermodynamic potential. This encoding is lossy (Gap 1).
- Cost Functional -> Physical Substrate: The fabricated material may not perfectly realize the intended potential. Fabrication tolerance introduces Gap 2.
- Physical State -> Intent Verification: Reading the equilibrium state requires measurement. Measurement is finite-precision (Axiom 4), introducing Gap 3.
The TAC does not eliminate vulnerabilities — it relocates them to the physics layer.
S5: The Vulnerability Lifecycle
5.1 Birth
A vulnerability is born when three conditions align:
- A gap exists between intent and specification (D_IS > 0)
- The gap is reachable by attacker input (reachability)
- The gap is unhandled (D_e > 0)
This is the conception of a vulnerability. It exists as a latent possibility — a path through the program that leads to an unintended state, waiting to be discovered.
5.2 Discovery
A vulnerability is discovered when an attacker (or researcher) finds the input sequence that traverses the gap. The discovery is a search problem over the input space.
5.3 Exploitation
A vulnerability is exploited when the attacker uses the gap to achieve a security violation — control flow hijack, information disclosure, privilege escalation.
5.4 Death (Patching)
A vulnerability dies when the programmer closes the gap — by adding a validation predicate, handling the error state, or removing the code path. But the patch itself is code with its own gaps. The lifecycle begins anew.
Birth (gap opens) -> Discovery (gap found) -> Exploitation (gap used) -> Death (gap closed)
|
v
New Birth (patch has new gaps)
5.5 The Eternal Return
By Theorem 2, the lifecycle is infinite for any evolving program. Every patch introduces new code, new gaps, and new vulnerabilities. This is not a failure of engineering — it is a consequence of the physics of computation.
S6: What This Means
6.1 For Bug Hunters
Stop looking for bugs. Bugs are accidents. Look for gaps — places where the programmer’s intent does not align with the machine’s behavior. Every gap is a potential vulnerability.
The Error Principle provides a methodology: measure the Information Debt, find the unhandled error states, compose them into chains. The vulnerabilities will emerge.
6.2 For Defenders
You cannot eliminate vulnerabilities. They are a mathematical necessity. You can only:
1. Reduce the gap (better specifications, formal methods)
2. Increase the cost of discovery (hardening, monitoring)
3. Limit the blast radius (sandboxing, least privilege)
4. Detect exploitation (anomaly detection, canaries)
The goal is not perfect security. The goal is to make exploitation more expensive than the value of the target.
6.3 For Academics
The Three-Gap Theorem provides a rigorous foundation for vulnerability theory. It connects:
– Information theory (entropy of gaps)
– Thermodynamics (Landauer’s principle, Second Law)
– Computation theory (Goedel, Turing, Rice)
– Security (exploitability, CVSS)
This is not a heuristic. It is a derivation from first principles.
S7: Summary of Theorems
| # | Theorem | Statement | Proof Basis |
|---|---|---|---|
| 0 | Three-Gap | D_IS > 0 AND D_SM > 0 AND D_MI > 0 | Goedel + Turing + Rice |
| 1 | Birth of Vulnerability | D_e > 0 implies exists exploit path | State-transition + pigeonhole |
| 2 | Inevitability | Sufficiently complex program has vulnerabilities | Infinite inputs vs finite checks |
Postscript: The Vulnerability Is the Rule, Not the Exception
The common narrative in security is: “Vulnerabilities are mistakes. If programmers were more careful, software would be secure.”
This document proves otherwise.
Vulnerabilities are not mistakes. They are structural features of the relationship between finite human intent and infinite physical dynamics. They exist because programs are physical objects governed by thermodynamics, not Platonic ideals governed by logic.
A perfectly secure program is not just difficult to write. It is physically impossible.
The Error Principle does not fix this. It names it. It measures it. And it shows how to find what the physics guarantees must exist.
Theorems 0-2 are proven from first principles. No empirical validation is required — they follow from the axioms. The challenge is to disprove the axioms, not the theorems.