Vulnerability Abundance: A formal proof of infinite vulnerabilities in code E. LEVERETT, Concinnity Risks Ltd., United Kingdom J. VAN DER HAM-DE VOS, University of Twente, Netherlands We present a constructive proof that a single C program—the Vulnerability Factory—admits a countably infinite set of distinct, independently CVE-assignable software vulnerabilities. We formalise the argument using elementary set theory, verify it against MITRE’s CVE Numbering Authority counting rules, sketch a model-checking analysis that corroborates unbounded vulnerability generation, and provide a Turing-machine characterisation that situates the result within classical computability theory. We then
arXiv:2604.07539v1 [cs.CC] 8 Apr 2026
contextualise this result within the long-running debate on whether undiscovered vulnerabilities in software are dense or sparse [16, 26, 28], and introduce the concept of vulnerability abundance: a quantitative analogy to chemical elemental abundance that describes the proportional distribution of vulnerability classes across the global software corpus. Because different programming languages render different vulnerability classes possible or impossible, and because language popularity shifts over time, vulnerability abundance is neither static nor uniform. Crucially, we distinguish between infinite vulnerabilities and the far smaller set of exploits: empirical evidence suggests that fewer than 6% of published CVEs are ever exploited in the wild, and that exploitation frequency depends not only on vulnerability abundance but on the market share of the affected software. We argue that measuring vulnerability abundance—and its interaction with software deployment—has practical value for both vulnerability prevention and cyber-risk analysis. We conclude that if one programme can harbour infinitely many vulnerabilities, the set of all software vulnerabilities is necessarily infinite, and we suggest the Vulnerability Factory may serve as a reusable proof artifact—a foundational “test object”—for future formal results in vulnerability theory. The complete source code is provided in the appendix under an MIT licence. ACM Reference Format: E. Leverett and J. van der Ham-de Vos. 2026. Vulnerability Abundance: A formal proof of infinite vulnerabilities in code. In New Security Paradigms Workshop (NSPW ’26). ACM, New York, NY, USA, 28 pages. https://doi.org/10.1145/nnnnnnn.nnnnnnn
1
Introduction
The question of whether software vulnerabilities are fundamentally finite or infinite is not merely academic. It determines whether exhaustive patching is a coherent security strategy or a Sisyphean labour. Dan Geer framed the dichotomy starkly: if vulnerabilities are sparse, then each one found and fixed meaningfully reduces exposure; if they are dense, then fixing one more is “essentially irrelevant to security” [14, 16]. This paper makes five contributions. First, we exhibit a concrete programme—a 622-line C artifact called the Vulnerability Factory—and prove rigorously that it can generate countably infinitely many distinct CVE-class vulnerabilities (Section 4). Second, we formalise and generalise the Vulnerability Factory as a Turing machine and show that its vulnerability-generating behaviour is a decidable, structurally transparent property, making it a reusable proof artifact for future formal work (Section 6). Third, we introduce the notion of vulnerability abundance (Section 7), a framework inspired by chemical elemental abundance that characterises the proportional distribution of vulnerability types across Authors’ Contact Information: E. Leverett, Concinnity Risks Ltd., Cambridge, United Kingdom, eleverett[at]concinnity-risks.com; J. van der Ham-de Vos, University of Twente, Enschede, Netherlands, j.vanderham[at]utwente.nl. Permission to make digital or hard copies of all or part of this work for personal or classroom use is granted without fee provided that copies are not made or distributed for profit or commercial advantage and that copies bear this notice and the full citation on the first page. Copyrights for components of this work owned by others than the author(s) must be honored. Abstracting with credit is permitted. To copy otherwise, or republish, to post on servers or to redistribute to lists, requires prior specific permission and/or a fee. Request permissions from [email protected]. © 2026 Copyright held by the owner/author(s). Publication rights licensed to ACM. Manuscript submitted to ACM
Manuscript submitted to ACM
1
2
E. Leverett and J. van der Ham-de Vos
the software ecosystem. Fourth, we carefully distinguish infinite vulnerabilities from the much smaller population of exploited vulnerabilities, and suggest how exploitation frequency depends on the interaction of vulnerability abundance with software market share (Section 8). Fifth, we argue that measuring these quantities has a concrete utility for predictive cyber risk (Section 10). Our result resolves the dense-versus-sparse debate constructively: we do not merely argue from complexity theory that vulnerabilities ought to be infinite; we exhibit a programme in which they provably are countably infinite.
2
Background and Related Work
2.1
Vulnerability Density: Dense or Sparse?
The foundational paper by Geer et al. [16]—co-authored with Schneier, Pfleeger, Quarterman, Metzger, Bace, and Gutmann—argued that Microsoft’s operating-system monoculture created systemic risk precisely because vulnerability density compounds with market share: what one machine has, so has every other. The implicit question was whether the stock of undiscovered vulnerabilities in a codebase is finite and declining, or effectively inexhaustible. While Bishop taught how to differentiate and record vulnerabilities[9], Anderson explored how hard it is to find bugs over time [4]. Ozment and Schechter [22] confirmed much of that by their study of the OpenBSD codebase over 7.5 years and 15 releases, finding a statistically significant decrease in the rate of foundational vulnerability reporting—but also a median vulnerability lifetime of at least 2.6 years. While this all suggests that mature codebases do improve, it does not demonstrate convergence to zero: the discovery rate declines, but new code continuously replenishes the reservoir. Rescorla [24] examined vulnerability discovery rates for Apache and IIS, modelling them as roughly linear over time and concluding that finding and fixing vulnerabilities may not substantially improve security. This linear model is consistent with a dense, non-depleting vulnerability population. Most recently, Spring and Illari [28] applied arguments from computability theory—including the halting problem and Rice’s theorem—to conclude that “there is no reason to believe undiscovered vulnerabilities are not essentially unlimited in practice.” Our constructive proof complements Spring’s theoretical argument with an explicit, executable, pedagogical, example.
2.2
Security Economics
Anderson [3] established the field of security economics by demonstrating that security failures are often misalignedincentive problems rather than purely technical ones. Anderson and Moore [6] and Anderson and Schneier [7] developed this programme further, showing that vulnerability persistence has economic explanations: the costs of exploitation are externalised, and defenders lack information about the vulnerability population proportions, the very vulnerability abundance we address later. Anderson’s Security Engineering [5] synthesises two decades of this work into a comprehensive textbook, observing that companies build vulnerable systems and governments look the other way because the economics reward precisely this behaviour. Our notion of vulnerability abundance extends this economic framing: if we can quantify which vulnerability types dominate, we can better align incentives toward the most impactful preventions. Manuscript submitted to ACM
Vulnerability Abundance: A formal proof of infinite vulnerabilities in code 2.3
3
A brief diversion into CVE Assignment Rules
The Common Vulnerabilities and Exposures (CVE) system, maintained by MITRE, assigns unique identifiers to publicly known vulnerabilities in published software. The CVE Counting Rules [19] specify that distinct vulnerabilities in distinct software components receive distinct CVE identifiers. Key criteria include: (1) the vulnerability must be independently fixable; (2) it must affect an identifiable codebase or component; and (3) if a single bug type appears in two separate products, each receives its own CVE. These rules are central to our proof: each generated module constitutes a distinct component with independently fixable vulnerabilities, satisfying the criteria for separate CVE assignment.
2.4
Formal Methods in Security
Formal verification—model checking, theorem proving, and abstract interpretation—has long been applied to securitycritical software [8]. While these methods can prove the absence of specific bug classes in bounded systems, Rice’s theorem guarantees that no general procedure can decide arbitrary semantic properties of programs [25]. Our Vulnerability Factory is designed to be trivially analysable though: the vulnerabilities are not hidden or obfuscated but intentionally transparent, making formal verification a mathematical confirmation rather than needing to run code to verify the number of vulnerabilities (Section 5).
2.5
Exploitation Rates and Prediction
Not all vulnerabilities are exploited though, and this is important even when they are infinite. Jacobs et al. [17] developed the Exploit Prediction Scoring System (EPSS) at FIRST.org, a data-driven model that estimates the probability of a CVE being exploited in the wild within 30 days. Empirical studies consistently find that exploitation is rare relative to the vulnerability population: Kenna Security [11] found 2.6% of tracked vulnerabilities exploited in 2019; and the Cyentia Institute [13] estimated approximately 6%. The RAND Corporation’s landmark study on zero-day vulnerabilities [1] found that the average zero-day lifespan was 6.9 years, with a median of 22 days to develop a functioning exploit. The discrepencies with these percentages have less to do with scientific dispute, and more to do when the studies were run. Since year on year growth of vulnerabilities ranges from 38% to 61%, it’s simply this growth over rather static exploitation numbers that defines the ranging values of these percentages. In short, we expect it to continue falling as we find more vulnerabilities that no one ever bothers to exploit heavily in the wild. These figures are essential context for our result: proving that vulnerabilities are infinite does not prove that exploits are infinite or that exploitation is unbounded. We should clearly spend more of our scientific energy predicting what vulnerabilities, software, networks, and organisations are most likely to be exploited. It is this differential cyber risk that can teach us the most...and yet we celebrate people who find these vulnerabilities instead of those who eliminate whole classes of them. If finding vulnerabilities is so laudable, then let us make a programme with infinite vulnerabilities; a transcendent weird machine to make our arguments concrete.
3
The Vulnerability Factory
The Vulnerability Factory is a self-contained C programme (vuln_factory.c, 622 lines; full source in Appendix B, released under the MIT licence) with two components: Base Set B. Eleven functions, each containing exactly one classic vulnerability drawn from a distinct CWE class: Manuscript submitted to ACM
4
E. Leverett and J. van der Ham-de Vos
ID
CWE Class
𝑏1
CWE-121 Stack Buffer Overflow
𝑏2
CWE-122 Heap Buffer Overflow
𝑏3
CWE-134 Format String
𝑏4
CWE-190 Integer Overflow
𝑏5
CWE-416 Use After Free
𝑏6
CWE-415 Double Free
𝑏7
CWE-78 OS Command Injection
𝑏8
CWE-367 TOCTOU Race
𝑏9
CWE-476 NULL Pointer Deref
𝑏 10
CWE-457 Uninitialised Variable
𝑏 11
CWE-22 Path Traversal
Generator 𝐺. On each execution, 𝐺 reads a persistent counter 𝑛 ∈ N, emits a new C source file vuln_module_𝑛.c containing five parameterised vulnerabilities, compiles it into a shared library, and increments 𝑛. Each module 𝑀𝑛 contains: ID
CWE / Parameterisation
𝑣𝑛,1
CWE-121: buffer size = 16 + 𝑛
𝑣𝑛,2
CWE-134: format string in module 𝑛
𝑣𝑛,3
CWE-190: threshold = INT_MAX − 𝑛
𝑣𝑛,4
CWE-416: allocation size = 8 + 𝑛
𝑣𝑛,5
CWE-78: injection in module 𝑛 context
The parameterisation by 𝑛 ensures that buffer sizes, overflow thresholds, heap layouts, and exploit payloads differ across modules. Our construction here of a C programme as a proof of existence, is inspired by Reflections on Trusting Trust[29]. In the pre-amble of that lovely paper there is a quote that mirrors our C programme delightfully: (1) This program can be easily written by another program. (2) This program can contain an arbitrary amount of excess baggage that will be reproduced along with the main algorithm. Here he is referring to the delicate and beautiful art of writing quines. Our vulnerability factory is not a quine per se but it can be easily written by another programme, and it too carries an arbitrary amount excess baggage. In this case the excess baggage is an infinite number of vulnerabilities! 4
Proof of Infinite Vulnerabilities
4.1
Formal Foundation
Definition 4.1 (Vulnerability). A vulnerability is a tuple (𝑐, 𝑡, 𝑝) where 𝑐 is a software component (identifiable compilation unit), 𝑡 is a CWE-classified weakness type, and 𝑝 is a parameter set that determines the specific exploit conditions. Two vulnerabilities (𝑐 1, 𝑡 1, 𝑝 1 ) and (𝑐 2, 𝑡 2, 𝑝 2 ) are distinct if 𝑐 1 ≠ 𝑐 2 or 𝑝 1 ≠ 𝑝 2 . Definition 4.2 (CVE-Assignability). A vulnerability (𝑐, 𝑡, 𝑝) is CVE-assignable if: (i) 𝑡 corresponds to a recognised CWE with established CVE precedent; (ii) 𝑐 is an identifiable software component; and (iii) the vulnerability is independently fixable without altering other components. Manuscript submitted to ACM
Vulnerability Abundance: A formal proof of infinite vulnerabilities in code
5
Definition 4.3 (The Vulnerability Factory’s Output). Let B = {𝑏 1, . . . , 𝑏 11 } be the base vulnerabilities. For each 𝑛 ∈ N, let 𝑀𝑛 denote the 𝑛-th generated module and define 𝑉 (𝑀𝑛 ) = {𝑣𝑛,1, 𝑣𝑛,2, 𝑣𝑛,3, 𝑣𝑛,4, 𝑣𝑛,5 }. The total vulnerability set is V =B ∪
∞ Ø
𝑉 (𝑀𝑛 ).
𝑛=0
4.2
Main Theorem
Theorem 4.4 (A Countable Infinity of Vulnerabilities). The set V is countably infinite, and every element of V is CVE-assignable. Proof. We establish the theorem via four claims. Claim 1 (Validity). Each 𝑣𝑛,𝑖 instantiates a CWE class (CWE-121, CWE-134, CWE-190, CWE-416, or CWE-78) with hundreds of prior CVE assignments. The generated code contains the canonical vulnerable pattern: strcpy into a fixed-size buffer without bounds checking (CWE-121), user input as a printf format argument (CWE-134), signed integer arithmetic exceeding INT_MAX (CWE-190), access to freed heap memory (CWE-416), and unsanitised input to system() (CWE-78). Each satisfies criterion (i) of CVE-assignability. Claim 2 (Distinctness). For 𝑚 ≠ 𝑛, modules 𝑀𝑚 and 𝑀𝑛 are compiled as separate shared libraries. Hence 𝑐𝑚 ≠ 𝑐𝑛 as software components. Moreover, the parameter sets differ: buffer sizes 16 + 𝑚 ≠ 16 + 𝑛, overflow thresholds INT_MAX − 𝑚 ≠ INT_MAX − 𝑛, and allocation sizes 8 + 𝑚 ≠ 8 + 𝑛. Each vulnerability requires a distinct exploit payload. By the CVE Counting Rules [19], distinct vulnerabilities in distinct components receive distinct identifiers. Therefore 𝑉 (𝑀𝑚 ) ∩ 𝑉 (𝑀𝑛 ) = ∅ for 𝑚 ≠ 𝑛, and each element satisfies criteria (ii) and (iii). Claim 3 (Unboundedness). After 𝑘 executions, the cardinality of the active vulnerability set is |V𝑘 | = |B| + Í𝑘 −1 𝑛=0 |𝑉 (𝑀𝑛 )| = 11 + 5𝑘. For any finite bound 𝐶 ∈ N, choosing 𝑘 > (𝐶 − 11)/5 yields |V𝑘 | > 𝐶. Since 𝐶 was arbitrary, |V | is not bounded by any finite number. Claim 4 (Countability). Define 𝑓 : N × {1, 2, 3, 4, 5} →
Ð
𝑛 𝑉 (𝑀𝑛 ) by 𝑓 (𝑛, 𝑖) = 𝑣 𝑛,𝑖 . This is a bijection from a countable
set. Since B is finite, V is countably infinite. Claims 1–4 together establish that V is a countably infinite set of CVE-assignable vulnerabilities. 4.3
□
Set-Theoretic Perspective
The vulnerability set V has cardinality ℵ0 . Since programmes are finite strings over a finite alphabet, the set of all programmes is countable, and therefore the set of all possible vulnerabilities across all possible programmes is at most countable. Our result thus achieves the theoretical maximum: the Vulnerability Factory saturates the countable bound. 4.4
CVE-Theoretic Analysis
Under MITRE’s CVE Counting Rules [19], two vulnerabilities receive separate CVE IDs when they are (a) independently discoverable, (b) independently fixable, and (c) attributable to distinct root causes or components. Each module 𝑀𝑛 satisfies all three: a researcher can identify its vulnerabilities without inspecting other modules; patching the buffer Manuscript submitted to ACM
6
E. Leverett and J. van der Ham-de Vos
overflow in 𝑀7 (bound 16 + 7 = 23) has no effect on 𝑀42 (bound 16 + 42 = 58); and each module compiles to a separate shared library. 4.5
Robustness to Partial Invalidation
A natural objection is that some subset of the generated vulnerabilities might fail to satisfy CVE assignment criteria under scrutiny. We show that the result is robust to any finite such invalidation. Lemma 4.5 (Cofinite Robustness). Let 𝑆 ⊆ V be the subset of vulnerabilities that fail some CVE-assignability criterion. If |𝑆 | < ℵ0 (i.e., 𝑆 is finite), then |V \ 𝑆 | = ℵ0 . Proof. This is immediate from cardinal arithmetic: removing a finite set from a countably infinite set yields a countably infinite set. Formally, if |V | = ℵ0 and |𝑆 | = 𝑘 for some 𝑘 ∈ N, then |V \ 𝑆 | = ℵ0 − 𝑘 = ℵ0 .
□
The practical consequence is that an objector cannot chip away at the result by identifying individual problematic instances. To bound the vulnerability count, one must demonstrate that cofinitely many—all but finitely many—fail the criteria. The result is thus also robust to the removal of entire CWE columns by the same logic. An example here will aid the understanding. Suppose a reviewer convincingly argues that an entire template—say, the format-string vulnerability 𝑣𝑛,2 —does not produce genuinely distinct CVEs across modules (perhaps because the exploitation mechanism is too similar across instantiations). Removing the entire column {𝑣𝑛,2 : 𝑛 ∈ N} still leaves four templates producing 4𝑘 vulnerabilities after 𝑘 iterations, which diverges. Invalidating the result requires showing that all five CWE templates fail the distinctness criterion simultaneously. Since the five templates span three fundamentally different vulnerability families—memory corruption (CWE-121, CWE-416), type confusion (CWE-190), and injection (CWE-134, CWE-78)—a single unified argument against all five would need to be extraordinarily broad. More crisply: let 𝐼 ⊆ {1, 2, 3, 4, 5} be the set of template indices that a reviewer successfully invalidates. The surviving vulnerability count after 𝑘 executions is (5 − |𝐼 |) · 𝑘, which diverges for any |𝐼 | < 5. The theorem fails only if |𝐼 | = 5. Let us just say that should this be the case we could obviously fix the vulnerability factory by using a different starting set of CWEs and publish again. Hopefully our focus then returns to the theoretic and we accept that the existence of this code serves as a signifier of the existence of a countable infinity of vulnerabilities rather than descend into CVE and CWE pedantry. It is the idea the c progam represents that is important, the Vulnerability Factory as a unit of future computing proofs. None-the-less let us lay out our own objections and how we overcame them. 4.6
Anticipated Objections
In which we address the most likely counterarguments to our proof. Objection 1: Parametric variation is not distinct root cause. A CNA might argue that all buffer overflows generated by the factory share a single root cause—strcpy without bounds checking—and that parametric variation in buffer size does not constitute a distinct vulnerability. Under this reading, all instantiations would receive a single CVE, not infinitely many. Response. The CVE counting rules [19] distinguish by component, not only by root-cause pattern. In practice, when the same vulnerability class appears in two separate shared libraries—even from the same vendor—CNAs assign separate Manuscript submitted to ACM
Vulnerability Abundance: A formal proof of infinite vulnerabilities in code
7
CVE identifiers. OpenSSL and LibreSSL routinely receive separate CVEs for structurally identical bug patterns. Each module 𝑀𝑛 compiles to a separate shared library, constituting a distinct component in any software inventory. Moreover, each instance requires a distinct exploit payload: the buffer sizes, heap layouts, and overflow thresholds all differ, so a working exploit for 𝑀7 will not work against 𝑀42 without modification. Independent fixability is also satisfied: one can patch 𝑀7 and ship a security advisory for it without touching 𝑀42 . Objection 2: Deliberate generation is tautological. A reviewer may argue that we have merely built a machine to produce vulnerabilities, and that this tells us nothing about vulnerabilities arising organically from programmer error. Response. The proof is existential, not causal. Set-theoretic cardinality is indifferent to the origin of set elements. Once the vulnerabilities exist—in compiled, loadable shared libraries—their provenance is irrelevant to their count. The CVE system does not distinguish between accidental and deliberate vulnerabilities: a vulnerability is a vulnerability regardless of whether it arose from a typo or an underhanded c contest1 . Furthermore, the Turing-machine characterisation in (Section 6) establishes that any Turing-complete system can host such a generator, so the construction is not an exotic edge case but a structural property of computation itself. Objection 3: Physical machines have bounded counters. The C implementation uses int for the iteration counter, which is bounded by INT_MAX (231 − 1 on most platforms). Therefore—the objection goes—the programme produces at most 11 + 5 × 231 ≈ 10.7 billion vulnerabilities, not infinitely many. Response. The proof operates over N, not over C’s int. The Turing-machine formulation (Definition 6.1) uses an unbounded counter tape, sidestepping the objection entirely. The C code is merely a pedagogical instantiation of the algorithm; the Turing Machine called Vulnerability Factory is the proof object. Nevertheless, even the bounded C implementation produces a vulnerability count (∼1010 ) that exceeds any practical vulnerability-management capacity by many orders of magnitude—a number that, while finite, is effectively inexhaustible for all operational purposes. One could also trivially replace int with arbitrary-precision arithmetic (e.g., GMP) to remove the bound in the implementation as well. Objection 4: There are only 11 vulnerabilities in this programme. One could argue that the programme submitted or the Turing Machine called Vulnerability Factory does not contain infinite vulnerabilities in its’ starting state or configuration. Response. Vulnerabilities are found in the execution paths not only in the source code, and some branches are not executed every time the programme is run. They are still vulnerabilities regardless of which inputs produce them, and this is why dynamic and static analysis are used for vulnerability hunting. So one would have to use a static analyser on the infinite iteration of executions, to see infinite vulnerabilities. This is precisely why mathematical and computational reasoning must demonstrate it converges towards infinity as N increases. We have a finite number of symbols for numbers too, but they can produce an infinity and we can reason about it. Bringing this back to the current argument, by allow the programme to use itself as input, we are generating the infinity of vulnerabilities within it. This is a fault of the Von Neumann architecture; data is code, and a Turing machine can read and print it’s own tape, which may itself contain new programmes. This logic is permitted in the Halting problem, why is it "unfair" in this paper?
1 https://en.wikipedia.org/wiki/Underhanded_C_Contest
Manuscript submitted to ACM
8
E. Leverett and J. van der Ham-de Vos
5
Formal Methods Corroboration
5.1
Static Analysis
Standard static analysers should detect the base vulnerabilities B without difficulty. More importantly, the generator 𝐺 is itself analysable: the template used to emit each module is visible in the source, and static analysis of the template confirms that every instantiation will contain the five prescribed vulnerabilities. 5.2
Model Checking
We model the Vulnerability Factory as a transition system T = (𝑆, 𝑠 0, →) where states 𝑠𝑘 = (𝑘, V𝑘 ) record the iteration counter and accumulated vulnerability set. The safety property “the vulnerability count is bounded by 𝐶” can be expressed in CTL as AG (|V | ≤ 𝐶). For any finite 𝐶, the model checker produces a counterexample trace of length ⌈(𝐶 − 11)/5⌉ + 1. 5.3
Decidability Considerations
Rice [25] established that no algorithm can decide an arbitrary non-trivial semantic property of programs. However, our Vulnerability Factory sidesteps this barrier elegantly: the vulnerabilities are structurally encoded in the source text, not emergent properties of complex computation. The programme is a proof witness—a constructive demonstration that circumvents the need for general decidability. In fact, general decidability prevented any hope of ever answering the question from an empirical point of view, and Spring’s paper forced us to invent a mathematical proof instead. 6
Turing Machine Characterisation
6.1
The Vulnerability Factory as a Turing Machine
To connect our result to the foundations of computability theory, we characterise the Vulnerability Factory as a Turing machine (TM). This formalisation serves two purposes: it demonstrates that the vulnerability-generation mechanism is computable in the classical sense, and it establishes the Vulnerability Factory as a reusable proof artifact—a “test object”—for future formal results. Of course it must all begin with being sure that a TM can be self-printing, and the work has already been done by Kicinsy and Varga[18]. So let us explore the Vulnerability Factory as a TM, while acknowledging we must change our choice of CWE: buffer overflows don’t exist in Turing Machine with infinite tape. Definition 6.1 (Vulnerability Factory TM). Define a Turing machine F = (𝑄, Γ, 𝑏, Σ, 𝛿, 𝑞 0, 𝐹 ) with the following behaviour. F has access to a work tape and a persistent counter tape encoding a natural number 𝑛 in binary. On input 𝜀 (the empty string), F executes the following cycle: (1) Read the counter tape to obtain 𝑛. (2) Generate: write to the output tape a syntactically valid Turing Machine 𝑆𝑛 containing any number of CWE vulnerability patterns parameterised by 𝑛. 2 (3) Increment: replace the contents of the counter tape with 𝑛 + 1. (4) Halt in an accepting state 𝑞 accept . 2 Not all CWEs are acceptable for this, for example CWE-798 (Hardcoded Credentials), CWE-259 (Hardcoded Password), and CWE-1188 (Insecure Default
Initialization) seem like they would NOT be infinitely generative. Plenty of others are though and an interesting choice here would be CWE-835 (Infinite Loop), both as constructor, but also as vulnerability. It would make a kind of monstrosity of a Vulnerability Factory and a Busy Beaver which we’ll call a Hecatoncheire vulnerability Factory. Though of course we leave such choices up to you dear reader, there are many Vulnerability Factories to explore. Manuscript submitted to ACM
Vulnerability Abundance: A formal proof of infinite vulnerabilities in code
9
Each invocation of F terminates in finite time (the output is 𝑂 (𝑛) characters, and all operations are elementary), but the counter persists across invocations, so that the 𝑘-th invocation produces module 𝑆𝑘 −1 . Theorem 6.2 (Computability of Vulnerability Generation). For every 𝑛 ∈ N, the output 𝑆𝑛 of F is computable and contains vulnerabilities, each distinct from the vulnerabilities in 𝑆𝑚 for all 𝑚 ≠ 𝑛. Proof. F performs only string concatenation and binary increment, both of which are primitive recursive. The output 𝑆𝑛 is a deterministic function of 𝑛 alone. The vulnerability patterns are syntactically fixed templates with 𝑛 their CVE-assignability follows from Claim 1 of Theorem 4.4, and their distinctness from Claim 2. 6.2
□
Relationship to Universal Turing Machines
A Universal Turing Machine (UTM) [31] can simulate any TM given its description. Since any F is a TM, a UTM can simulate F and thereby generate the infinite vulnerability sequence. This observation has a conceptual consequence: any sufficiently powerful computing system can host a vulnerability factory. More precisely, any system capable of universal computation—any language that is Turing-complete—can implement the vulnerability-generation cycle of Definition 6.1. Some vulnerabilities are an artifact of C’s memory model; but we believe others are an artifact of Von Neumann architectures where code and data is mixed in memory3 . A Turingcomplete language that eliminates memory-corruption vulnerabilities (e.g., Rust, Haskell) can still implement a generator that emits vulnerable C code, or that generates vulnerabilities native to its own type system (injection, logic errors, deserialisation flaws). The specific CWE classes change; the countable infinity would not for any language, including assembly. 6.3
The Vulnerability Factory as a Proof Artifact
We suggest that F (and its concrete implementation as vuln_factory.c) may serve as a foundational proof artifact for future formal results in vulnerability theory, much as specific Turing machines serve as proof artifacts in computability theory. Just as the Busy Beaver function Σ(𝑛) provides a concrete object for studying the limits of computability, and the halting problem’s proof relies on a specific self-referential machine, the Vulnerability Factory provides a concrete, executable witness for the infinitude of software vulnerabilities. Potential applications include: (1) Lower bounds on vulnerability scanning. Any tool that claims to find “all” vulnerabilities in arbitrary code must, in principle, handle the output of F . Since the output is unbounded, no finite-time scanner can be exhaustive—a result that follows from Rice’s theorem [25] but is made vivid by F as a concrete counterexample. (2) Impossibility results for vulnerability databases. Any finite database that claims completeness over a corpus containing the Vulnerability Factory’s output is provably incomplete. (3) Benchmarking formal verification tools. The generated modules provide an infinite family of structurally similar but parametrically distinct test cases, useful for evaluating the scalability of static analysers and model checkers. (4) Foundations for vulnerability economics. The Vulnerability Factory’s linear growth function 𝑇 (𝑘) = 11 + 5𝑘 provides a clean model for studying how vulnerability counts interact with patching rates, discovery rates, and economic incentives. Other growth rates or limits can now be explored by generating different vulnerability factories. 3 Note that the so-called Harvard Architecture does not solve this problem[23]
Manuscript submitted to ACM
10
E. Leverett and J. van der Ham-de Vos (5) Compositional reasoning. If F1 and F2 are two vulnerability factories generating disjoint CWE classes, their composition F1 ∥F2 generates vulnerabilities from the union of classes, with the total count growing at rate |𝑉1 | + |𝑉2 | per invocation. This compositional structure may prove useful in modelling real-world software systems as compositions of vulnerable components.
Remark. The Vulnerability Factory is deliberately transparent: its vulnerabilities are not hidden, obfuscated, or emergent. This transparency is a feature, not a limitation. In computability theory, the most powerful proof artifacts are often the simplest: Turing’s original halting-problem proof uses a straightforward diagonalisation argument, not a complex construction. Similarly, the power of the Vulnerability Factory lies not in the subtlety of its vulnerabilities but in the rigour of its generative mechanism and the clarity with which it demonstrates a countable infinitude. In an effort to keep our work sustainable we leave any uncountable infinities of vulnerabilities for future generations to discover or prove. We could not think of a way to order vulnerabilities, and thus any approach by diagonalisation is deterred. Perhaps future generations are smarter and wiser, and can find a way where we could not. Standing on the shoulders of giants is all well and good, but there is an art to not stepping on their toes on the way up. 7
Vulnerability Abundance
The power of this idea is not really the proof, it is how it changes the world we live in, what it implies. If we have an abundance, then we should map it differently, and move beyond simply counting vulnerabilities. An analogy here may helps us reason in this new and bewildering universe. 7.1
The Chemical Abundance Analogy
In chemistry, elemental abundance describes the proportional occurrence of each element in a given environment—the universe, the solar system, the Earth’s crust. Hydrogen constitutes roughly 73% of baryonic mass in the universe; oxygen dominates the Earth’s crust at 46% by mass. These proportions are not arbitrary: they reflect the physical processes that produced them—Big Bang nucleosynthesis, stellar fusion, supernova nucleosynthesis [2]. We propose an analogous concept for software vulnerabilities. Definition 7.1 (Vulnerability Abundance). The vulnerability abundance of a CWE class 𝑡 in a software corpus Σ at time 𝜏 is |{𝑣 ∈ V (Σ, 𝜏) : type(𝑣) = 𝑡 }| |V (Σ, 𝜏)| where V (Σ, 𝜏) is the set of all vulnerabilities (discovered and undiscovered) in Σ at time 𝜏. 𝐴Σ (𝑡, 𝜏) =
Just as elemental abundances vary between the Sun and the Earth’s crust because different physical processes dominate, vulnerability abundances vary between software corpora because different linguistic and architectural processes dominate. 7.2
Programming Language as Nucleosynthesis
Different programming languages make different vulnerability classes structurally possible or impossible, much as different stellar processes produce different elements. Manuscript submitted to ACM
Vulnerability Abundance: A formal proof of infinite vulnerabilities in code
11
Memory-unsafe languages (C, C++). These are the “hydrogen furnaces” of the vulnerability universe. They enable the full spectrum of memory corruption vulnerabilities: buffer overflows (CWE-121, CWE-122), use-after-free (CWE-416), double free (CWE-415), and uninitialised reads (CWE-457). Google and Microsoft have independently reported that approximately 70% of their security vulnerabilities stem from memory safety errors [20]. Memory-safe languages (Rust, Go, Java, Python). These correspond to lighter nucleosynthetic pathways: they produce a narrower but still significant spectrum of vulnerabilities. Rust’s ownership model eliminates use-after-free and buffer overflows in safe code, but injection attacks (CWE-78, CWE-89), logic errors, and concurrency bugs persist [27]. Java eliminates pointer arithmetic but introduces deserialisation vulnerabilities (CWE-502). Python eliminates memory corruption but is susceptible to code injection (CWE-94) via eval() and pickle. Web languages (JavaScript, PHP, SQL).. These produce a distinct “elemental spectrum” dominated by cross-site scripting (CWE-79), SQL injection (CWE-89), and server-side request forgery (CWE-918). The analogy extends further: just as the periodic table has gaps that were predicted before the elements were discovered (Mendeleev’s eka-elements), one can predict vulnerability classes that should exist in a language based on its type system and memory model, even before specific instances are found. Do compiled programmes or source code have "vulnerability spectra"? The insight here is that perhaps every programme has a vulnerability factory in it; emitting vulnerabilities of different types with varied probabilities. At least this conceptually is useful, to help use understand the relationship between what we have found and what remains. Perhaps the battle ground is the programming language design, and the "spectra" will teach us much about future programming language security. Then how will things change over time? 7.3
Temporal Dynamics
Chemical abundances in the universe change over cosmological time: the proportion of heavy elements increases as successive generations of stars process primordial hydrogen. Similarly, vulnerability abundance changes over time as the global software corpus evolves. The TIOBE Programming Community Index [30] tracks language popularity. As of early 2026, Python leads, with C and C++ holding strong second and third positions despite the U.S. government’s recommendation to migrate to memory-safe languages [21]. If this migration occurs at scale, we would predict: a secular decline in memory-corruption vulnerability abundance; a relative increase in logic-error and injection vulnerability abundance; and a transient spike in interoperability vulnerabilities at language boundaries (FFI, unsafe blocks). Moreover, the types of software we write influence abundance. The rise of web applications inflated XSS and SQL injection proportions; the rise of IoT inflates firmware and protocol-level vulnerability classes; the rise of machine learning introduces model poisoning and adversarial input classes that had negligible abundance a decade ago. 7.4
Abundance Is Not Uniform
Vulnerability abundance across all codebases is almost certainly not uniformly distributed. The proportions depend on at least three factors: (1) language prevalence—the market share of programming languages determines which vulnerability classes are even possible in the majority of code; (2) application domain—financial software faces different vulnerability spectra than embedded firmware; and (3) developer practice—the adoption of static analysis, fuzzing, and code review Manuscript submitted to ACM
12
E. Leverett and J. van der Ham-de Vos
selectively reduces certain vulnerability types. This non-uniformity is precisely what makes vulnerability abundance worth measuring, exploring, and reasoning about.
8
Infinite Vulnerabilities, Finite Exploits
8.1
The Exploitation Gap
Our proof establishes that vulnerabilities are at least countably infinite across all software. It is essential to note that this does not imply that any individual piece of software has infinite vulnerabilities, or that exploits are infinite, nor that exploitation is unbounded. Explicitly, it may still be possible to find and patch all vulnerabilities in a particular piece of well engineered software. The relationship between vulnerabilities and exploits is analogous to the relationship between chemical elements and industrial applications: the periodic table contains 118 known elements, but only a handful dominate commerce and engineering. Moreover, an exploit isn’t worth anything if it doesn’t "react" with a deployed system. It may be more useful in one time period than another, precisely because of the ratio of deployed systems with that exposed vulnerability. Like a chemical reaction, you need both the exposed vulnerability and the exploit in the right amounts to be highly impactful. Empirical evidence consistently shows that exploitation is rare: Source
%
Period
Kenna Security [11]
2.6%
2019
Cyentia/FIRST [13]
∼6%
cumulative
CISA’s Known Exploited Vulnerabilities (KEV) catalogue [12] contained 1,484 entries by the end of 2025, out of over 200,000 published CVEs—less than 0.75%. Of those CVEs that are exploited, Kenna [11] found that only 6% of the exploited subset ever reached widespread exploitation (affecting more than 1 in 100 organisations). Will it become less rare? Will we get better at detecting it? In the fullness of time this will be revealed, yet we expect some general principles to uphold over time.
8.2
Exploit Development Is Costly
The RAND study [1] found a median time of 22 days to develop a functioning exploit, with substantial variation. Exploit development requires vulnerability-specific knowledge: the buffer size, the heap layout, the instruction set, the mitigations in place. Each exploit is a bespoke artifact, and the economics of bespoke production are fundamentally different from mass production, though this may change quickly with the application of AI. Where the Vulnerability Factory generates vulnerabilities at essentially zero marginal cost, exploit development has non-trivial per-unit cost. This asymmetry—cheap vulnerability creation, expensive exploit development—is a structural feature of the security landscape. If you don’t believe that to be true, try to exploit all the vulnerabilities in the factory, perhaps writing one that is harder to exploit yourself, and let us know the results.
8.3
Market Share as a Multiplier
Even when exploitation is rare, its impact can be enormous if the vulnerable software is widely deployed or highly valuable. Manuscript submitted to ACM
Vulnerability Abundance: A formal proof of infinite vulnerabilities in code
13
Definition 8.1 (Exploitation Exposure). The exploitation exposure of a vulnerability 𝑣 in software 𝑠 is 𝐸 (𝑣, 𝑠) = 𝐴(𝑡 𝑣 ) × 𝐷 (𝑠) × 𝑃exploit (𝑣) where 𝐴(𝑡 𝑣 ) is the vulnerability abundance of 𝑣’s CWE type, 𝐷 (𝑠) is the deployment share of software 𝑠, and 𝑃exploit (𝑣) is the probability that 𝑣 is exploited. Consider a vulnerability with low abundance—say, 𝐴(𝑡 𝑣 ) = 0.01%. If the affected software commands 50% market share, then even a single working exploit exposes half of all reachable machines. Conversely, a vulnerability in the most abundant class (𝐴(𝑡 𝑣 ) = 30%) affecting software with 0.1% market share produces negligible aggregate exposure. This is directly analogous to chemical applications: lithium is rare in the Earth’s crust (∼0.002%), yet its role in batteries gives it outsized economic importance. Vulnerability abundance alone does not determine risk; deployment abundance acts as a multiplier. Geer et al. [16] identified precisely this dynamic: the danger of Microsoft’s dominance was not merely that Windows had vulnerabilities, but that its market share meant each vulnerability had maximal reach. He also explored this idea in On Market Concentration and Risk[15], though that was focussed more at the organisation than the software. The principles apply regardless, and we believe the result of this paper will have powerful ramifications for the vulnerability equities process (VEP) of any country[10]. 8.4
Saturation and the Small-Exploit Principle
A very small number of exploits can saturate the reachable machine population. If three or four software stacks account for 90% of deployed machines, then one exploit per stack suffices to place 90% at risk. The attacker needs only enough exploits to cover the dominant deployment shares. This is the small-exploit principle: the number of exploits required for broad coverage is bounded not by the number of vulnerabilities (which we now know is infinite) but by the number of dominant software monocultures (which is small). The practical risk landscape is shaped by the convolution of two distributions: the long-tailed abundance of vulnerability types and the heavy-tailed concentration of software deployment. 9
From One Programme to All Software Theorem 9.1 (Software Vulnerabilities Are Infinite). The set of all vulnerabilities across all software is countably
infinite. Proof. Let Π denote the set of all software programmes. By Theorem 4.4, there exists a programme 𝜋 ∗ ∈ Π (the Ð Vulnerability Factory) such that V (𝜋 ∗ ) is countably infinite. Since V (𝜋 ∗ ) ⊆ 𝜋 ∈Π V (𝜋), the set of all software vulnerabilities contains a countably infinite subset and is therefore infinite. Moreover, since programmes are finite strings over a finite alphabet, Π is countable. Each V (𝜋) is at most countable. A countable union of countable sets is countable. Hence the set of all software vulnerabilities is exactly ℵ0 .
□
Remark. This proof is constructive: we exhibit a computable witness. Like Cantor’s diagonal argument or Turing’s halting-problem proof, the power lies in exhibiting a concrete object with the desired property. Corollary 9.2. No finite vulnerability database can ever be complete. Manuscript submitted to ACM
14
E. Leverett and J. van der Ham-de Vos
10
Applications and Implications
10.1
Vulnerability Prevention
If vulnerability abundance can be measured with reasonable accuracy, security investment can be directed toward the most abundant classes. The chemical analogy suggests a methodological programme: just as geochemists survey elemental abundances to understand planetary formation, security researchers could survey vulnerability abundances across representative corpora to understand the “geology” of the software landscape. Anderson [3] argued that security failures are fundamentally economic. Vulnerability abundance data could sharpen this analysis: if 70% of vulnerabilities in C/C++ codebases are memory-safety errors, then the expected return on investment from adopting Rust is quantifiable. 10.2
Cyber-Risk Analysis
Vulnerability abundance, combined with the exploitation-exposure model of Section 8.3, provides a structural framework for cyber-risk assessment. Given a target organisation’s technology stack, one can estimate the expected vulnerability spectrum and, by combining it with empirical exploitation rates [13, 17], derive a probabilistic risk profile. Crucially, the market-share multiplier means that organisations running dominant software stacks face correlated risk: when an exploit emerges for a widely-deployed component, it affects all organisations simultaneously—precisely the systemic risk that Geer et al. [16] warned about. 10.3
The Dense World
Rescorla [24] asked whether finding security holes is a good idea. Ozment [22] offered cautious optimism. But our result—and Spring’s complementary analysis [28]—suggests the optimism must be tempered. Vulnerabilities are dense in the sense of countably infinite. Yet density of vulnerabilities does not entail density of exploitation. The empirical record shows fewer than 6% are ever exploited. The infinite ocean of vulnerabilities is navigated by a finite—and surprisingly small—fleet of exploits. But that small fleet, guided by market share, can reach nearly every shore. The correct framing is not “how many vulnerabilities remain?” but “what is the abundance distribution, how does it interact with deployment, and how can we shift both?” 11
Conclusion
We have proven that the Vulnerability Factory—a single, short C programme—harbours countably infinitely many distinct, CVE-assignable vulnerabilities. By elementary set inclusion, this implies that the set of all software vulnerabilities is infinite. We have formalised the programme as a Turing machine, showing that its vulnerability-generating behaviour is computable and structurally transparent, and we have suggested that it may serve as a reusable proof artifact for future results in vulnerability theory. We introduced the concept of vulnerability abundance as a framework for understanding the proportional distribution of vulnerability types, drawing an analogy to chemical elemental abundance. Just as stellar nucleosynthesis determines which elements dominate the cosmos, programming language choice determines which vulnerability classes dominate the software ecosystem. We have been careful to distinguish infinite vulnerabilities from finite exploits. The market share of affected software acts as a powerful multiplier: a single exploit against a dominant platform achieves broader reach than thousands Manuscript submitted to ACM
Vulnerability Abundance: A formal proof of infinite vulnerabilities in code
15
of exploits against niche software. A small number of exploits suffices to saturate the machine population, because deployment is concentrated while vulnerabilities are dispersed. The task is not to empty the ocean but to chart its currents—to understand which vulnerabilities are abundant, which are rare, how deployment concentrates risk, and how the proportions are shifting. Vulnerability abundance, we submit, is the right framework for that charting.
Acknowledgements The author thanks the cybersecurity economics community—in particular the late Ross Anderson, Dan Geer, Jon Crowcroft, and Bruce Schneier—whose decades of work created the intellectual context for this paper. They also thank Eiko Yoneki, Sergey Bratus, Marion Marschalek, Jay Jacobs, Art Manion, Sam Marsden, and Erin Burns for their forbearance and encouragement. Last but not least our families for kindly enduring dinner table discussions that bored them but interested us. The Vulnerability Factory code is released under the MIT licence and should not be deployed in any production environment. If you appreciate it, you can lobby your favourite CNA to give the authors 𝐶𝑉 𝐸 − 2026 − ∞. References [1] Lillian Ablon and Andy Bogart. 2017. Zero Days, Thousands of Nights: The Life and Times of Zero-Day Vulnerabilities and Their Exploits. Technical Report RR-1751-RC. RAND Corporation. https://www.rand.org/pubs/research_reports/RR1751.html [2] Edward Anders and Nicolas Grevesse. 1989. Abundances of the Elements: Meteoritic and Solar. Geochimica et Cosmochimica Acta 53, 1 (1989), 197–214. doi:10.1016/0016-7037(89)90286-X [3] Ross Anderson. 2001. Why Information Security is Hard—An Economic Perspective. In Proceedings of the 17th Annual Computer Security Applications Conference (ACSAC). IEEE, 358–365. https://www.acsac.org/2001/papers/110.pdf [4] Ross Anderson. 2002. Security in Open versus Closed Systems - The Dance of Boltzmann, Coase and Moore. Technical Report. Cambridge University, England. [5] Ross Anderson. 2020. Security Engineering: A Guide to Building Dependable Distributed Systems (3rd ed.). Wiley. [6] Ross Anderson and Tyler Moore. 2006. The Economics of Information Security. Science 314, 5799 (2006), 610–613. doi:10.1126/science.1130992 [7] Ross Anderson and Bruce Schneier. 2005. Guest Editors’ Introduction: Economics of Information Security. IEEE Security & Privacy 3, 1 (2005), 12–13. doi:10.1109/MSP.2005.14 [8] David Basin et al. 2023. Formal Methods for Security. CyBOK—The Cyber Security Body of Knowledge (2023). https://www.cybok.org/media/ downloads/Formal_Methods_for_Security_v1.0.0.pdf [9] Matt Bishop. 1999. Vulnerabilities Analysis. In Proceedings of the Second International Symposium on Recent Advances in Intrusion Detection. 125–136. [10] Tristan Caulfield, Christos Ioannidis, and David Pym. 2017. The US vulnerabilities equities process: An economic perspective. In International Conference on Decision and Game Theory for Security. Springer, 131–150. [11] Cisco Kenna Security. 2022. Cisco’s Kenna Security Research Shows the Relative Likelihood of an Organization Being Exploited. Cisco Newsroom. https://newsroom.cisco.com/c/r/newsroom/en/us/a/y2022/m01/cisco-kenna-security-research-shows-the-relative-likelihood-ofan-organization-being-exploited.html [12] Cybersecurity and Infrastructure Security Agency. 2025. Known Exploited Vulnerabilities Catalog. https://www.cisa.gov/known-exploitedvulnerabilities-catalog. 1,484 entries as of end of 2025. [13] Cyentia Institute and FIRST. 2024. A Visual Exploration of Exploitation in the Wild. Cyentia Institute. https://www.cyentia.com/epss-study/ [14] Daniel Geer. 2014. Cybersecurity as Realpolitik. In Black Hat USA 2014, Keynote Address. Las Vegas, NV. https://geer.tinho.net/geer.blackhat.6viii14.txt [15] Dan Geer, Eric Jardine, and Eireann Leverett. 2020. On market concentration and cybersecurity risk. Journal of Cyber Policy 5, 1 (2020), 9–29. doi:10.1080/23738871.2020.1728355 [16] Daniel Geer, Charles P. Pfleeger, Bruce Schneier, John S. Quarterman, Perry Metzger, Rebecca Bace, and Peter Gutmann. 2003. CyberInsecurity: The Cost of Monopoly—How the Dominance of Microsoft’s Products Poses a Risk to Security. Technical Report. Computer and Communications Industry Association. https://ccianet.org/wp-content/uploads/2003/09/cyberinsecurity.pdf [17] Jay Jacobs, Michael Roytman, Sasha Romanosky, Benjamin Edwards, and Idris Adjeril. 2021. Exploit Prediction Scoring System (EPSS). Digital Threats: Research and Practice 2, 3 (2021), Article 3. doi:10.1145/3436242 Also available as arXiv:1908.04856. [18] Richárd Kicsiny and Zoltán Varga. 2023. A self-printing Turing machine program with the possibility of containing any other program. (Oct. 2023). doi:10.21203/rs.3.rs-3399020/v1 Manuscript submitted to ACM
16
E. Leverett and J. van der Ham-de Vos
[19] MITRE Corporation. 2024. CVE Counting Rules and Guidance. https://www.cve.org/ResourcesSupport/AllResources/CNARules Accessed February 2026. [20] National Security Agency. 2022. Software Memory Safety. Technical Report. Cybersecurity Information Sheet. https://media.defense.gov/2022/Nov/ 10/2003112742/-1/-1/0/CSI_SOFTWARE_MEMORY_SAFETY.PDF [21] Office of the National Cyber Director. 2024. Back to the Building Blocks: A Path Toward Secure and Measurable Software. Technical Report. The White House. https://www.whitehouse.gov/oncd/briefing-room/2024/02/26/press-release-technical-report/ [22] Andy Ozment and Stuart E. Schechter. 2006. Milk or Wine: Does Software Security Improve with Age?. In Proceedings of the 15th USENIX Security Symposium. USENIX Association, Vancouver, BC, Canada. https://www.usenix.org/legacy/event/sec06/tech/full_papers/ozment/ozment.pdf [23] Richard Pawson. 2022. The Myth of the Harvard Architecture . IEEE Annals of the History of Computing 44, 03 (July 2022), 59–69. doi:10.1109/ MAHC.2022.3175612 [24] Eric Rescorla. 2005. Is Finding Security Holes a Good Idea? IEEE Security & Privacy 3, 1 (2005), 14–19. doi:10.1109/MSP.2005.17 [25] Henry Gordon Rice. 1953. Classes of Recursively Enumerable Sets and Their Decision Problems. Trans. Amer. Math. Soc. 74, 2 (1953), 358–366. doi:10.2307/1990888 [26] Bruce Schneier. 2015. How Many Vulnerabilities? Schneier on Security (blog). https://www.schneier.com/blog/archives/2015/04/how_many_vulner. html [27] SEI CERT. 2024. Rust Software Security: A Current State Assessment. Carnegie Mellon University, Software Engineering Institute Blog. https: //www.sei.cmu.edu/blog/rust-software-security-a-current-state-assessment/ [28] Jonathan M. Spring and Phyllis Illari. 2023. An Analysis of How Many Undiscovered Vulnerabilities Remain in Information Systems. Computers & Security 131 (2023), 103290. doi:10.1016/j.cose.2023.103290 Also available as arXiv:2304.09259. [29] Ken Thompson. 1984. Reflections on trusting trust. Commun. ACM 27, 8 (Aug. 1984), 761–763. doi:10.1145/358198.358210 [30] TIOBE Software BV. 2026. TIOBE Programming Community Index. https://www.tiobe.com/tiobe-index/. Accessed February 2026. [31] Alan M. Turing. 1936. On Computable Numbers, with an Application to the Entscheidungsproblem. Proceedings of the London Mathematical Society s2-42, 1 (1936), 230–265. doi:10.1112/plms/s2-42.1.230
Manuscript submitted to ACM
Vulnerability Abundance: A formal proof of infinite vulnerabilities in code A
17
Building and Running the Vulnerability Factory
A.1
Prerequisites
The Vulnerability Factory requires a POSIX-compatible system with a C compiler (gcc or clang), make, and POSIX dlopen support (standard on Linux and macOS). Tested on Linux (glibc, GCC 12+) and macOS (Apple Clang 15+). A.2 1 2 3 4 5 6 7 8
Compilation
CC = cc CFLAGS = -g -Wno - format - security -Wno - deprecated - declarations UNAME_S := $( shell uname -s) ifeq ($( UNAME_S ) , Linux ) LDFLAGS = - ldl else LDFLAGS = endif
9 10
all : vuln_factory
11 12 13
vuln_factory : vuln_factory .c $( CC ) $( CFLAGS ) -o $@ $ < $( LDFLAGS )
14 15
clean : rm -f vuln_factory
16 17 18 19 20
reset : clean rm -rf vuln_modules rm -f vuln_counter . txt
Listing 1. Makefile
To compile: make A.3
Safe Execution
Warning: This programme is intentionally vulnerable and should never be deployed on a network-accessible machine or run with elevated privileges. Recommended safety measures: (1) Run inside a disposable virtual machine or container (Docker, QEMU, or a cloud sandbox). (2) Do not run as root. (3) Disable network access if possible. (4) Use make reset to clean generated modules after experimentation. (5) Consider running under seccomp, AppArmor, or a similar MAC framework. To run: ./vuln_factory Each execution generates one new vulnerable module in vuln_modules/. Use menu option 4 for a vulnerability census. Use make reset to remove all generated modules. A.4
Licence
The Vulnerability Factory is released under the MIT Licence. See the licence header in the source file (Appendix B).
Manuscript submitted to ACM
18 B
E. Leverett and J. van der Ham-de Vos Source Code: vuln_factory.c
The complete, unabridged source code follows. Line numbers correspond to the original file. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
vuln_factory .c
—
The Infinite Vulnerability Factory
Permission is hereby granted , free of charge , to any person obtaining a copy of this software and associated documentation files ( the " Software ") , to deal in the Software without restriction , including without limitation the rights to use , copy , modify , merge , publish , distribute , sublicense , and / or sell copies of the Software , and to permit persons to whom the Software is furnished to do so , subject to the following conditions : The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software . THE SOFTWARE IS PROVIDED " AS IS ", WITHOUT WARRANTY OF ANY KIND , EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE . EDUCATIONAL PURPOSE ONLY
—
DO NOT DEPLOY
This program is a teaching tool that demonstrates how a single piece of software can contain a theoretically infinite number of distinct , CVE - worthy vulnerabilities . ================================================================ MECHANISM ================================================================ 1. The base program contains 11 classic vulnerability types , each mapping to a well - known CWE with extensive CVE history . 2. Each execution generates a NEW C source file containing 5 additional vulnerabilities , compiles it into a shared library , and dynamically loads it . 3. Each generated module 's vulnerabilities are parameterized by an iteration counter N , making every instance exploitably distinct from every other . 4. Since N is unbounded , the total number of vulnerabilities grows without limit . ================================================================ PROOF : THIS PROGRAM CONTAINS INFINITELY MANY VULNERABILITIES ================================================================ Theorem . The Vulnerability Factory can produce a countably infinite number of distinct , independently CVE - assignable vulns . Definitions : - Let B = { b_1 , ... , b_11 } be the set of base vulnerabilities . - Let M_n be the module generated on the n - th execution (n in N). - Let V( M_n ) = { v_ {n ,1} , ... , v_ {n ,5}} be the vulns in M_n .
Manuscript submitted to ACM
Vulnerability Abundance: A formal proof of infinite vulnerabilities in code
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
19
* * Claim 1 ( Validity ): Each v_ {n ,i} is a genuine CVE - class vuln . * v_ {n ,1}: CWE -121 stack buffer overflow ( strcpy , no bounds check ) * v_ {n ,2}: CWE -134 format string ( user input as fmt arg ) * v_ {n ,3}: CWE -190 integer overflow ( signed int arithmetic ) * v_ {n ,4}: CWE -416 use - after - free ( access after free ) * v_ {n ,5}: CWE -78 command injection ( unsanitized system () ) * Each CWE class has hundreds of real - world CVEs . * * Claim 2 ( Distinctness ): For m != n , V( M_m ) and V( M_n ) are disjoint . * - Each module is a separate shared library ( distinct component ). * - Buffer sizes , overflow thresholds , and alloc sizes all differ . * - Each requires a different exploit payload . * - Each can be independently patched without affecting others . * - Per CVE Numbering Authority rules , distinct vulnerabilities * in distinct components receive distinct CVE IDs . * * Claim 3 ( Unboundedness ): * After k executions , total vulns >= |B| + 5k = 11 + 5k. * For any finite bound C , choose k > (C - 11) /5. * Then total vulns > C. Since C was arbitrary , the limit is infinite . * * Claim 4 ( Countability ): * The map f: N x {1..5} -> V given by f(n ,i) = v_ {n ,i} is a * bijection from a countable set to the generated vulnerabilities . * Together with the finite base set B , the full set is countable . * * Therefore the program admits countably infinitely many distinct , * independently CVE - assignable vulnerabilities . QED * * ================================================================ * COROLLARY : VULNERABILITY DENSITY * ================================================================ * * After k runs , the program actively loads 11 + 5k vulnerabilities . * The " vulnerability density " ( vulns per line of active code ) grows * monotonically . Students can compute this as an exercise . * * ================================================================ * BUILDING AND RUNNING * ================================================================ * * make * ./ vuln_factory * * Requires : cc ( gcc or clang ) , make , POSIX ( dlopen ) * Tested on : Linux ( glibc ) , macOS ( Apple clang ) */
109 110 111 112 113 114 115 116 117
# include # include # include # include # include # include # include # include
< stdio .h > < stdlib .h > < string .h > < unistd .h > < dlfcn .h > < dirent .h > < sys / stat .h > < limits .h >
118 119 120 121
# define INPUT_BUFSZ 4096 # define MODULES_DIR " ./ vuln_modules " # define COUNTER_FILE " ./ vuln_counter . txt "
122 123
/* Platform - specific shared library settings */
Manuscript submitted to ACM
20
124 125 126 127 128 129 130 131 132 133 134
E. Leverett and J. van der Ham-de Vos
# ifdef __APPLE__ # define SHLIB_EXT ". dylib " # define COMPILE_SHLIB_FMT \ " cc ␣ - dynamiclib ␣ -Wno - format - security ␣ -Wno - deprecated - declarations ␣" \ " -o␣ '%s '␣ '%s '␣ 2 >/ dev / null " # else # define SHLIB_EXT ". so " # define COMPILE_SHLIB_FMT \ " cc ␣ - shared ␣ - fPIC ␣ -Wno - format - security ␣ -Wno - deprecated - declarations ␣" \ " -o␣ '%s '␣ '%s '␣ 2 >/ dev / null " # endif
135 136 137 138 139 140
/* Forward declarations */ static int read_counter ( void ); static void write_counter ( int n); static void generate_module ( int n); static int load_and_run_modules ( const char * input );
141 142 143 144 145 146 147
/* * * * * *
================================================================ SECTION 1: BASE VULNERABILITIES (11 distinct CWE classes ) Each function below contains exactly one classic vulnerability . Together they form the finite base set B in the proof above . ================================================================ */
148 149 150 151 152 153 154 155 156 157 158 159 160
/* * B1 — CWE -121: Stack - based Buffer Overflow * * strcpy performs no bounds checking . If | input | > 63 , the write * overflows buf [] and overwrites adjacent stack memory , including * the saved return address . */ void vuln_stack_overflow ( const char * input ) { char buf [64]; strcpy ( buf , input ); printf ("␣␣[ B1 ]␣ Stack ␣ overflow ␣ processed ␣% zu ␣ bytes \n" , strlen ( buf )); }
161 162 163 164 165 166 167 168 169 170 171 172 173 174
/* * B2 — CWE -122: Heap - based Buffer Overflow * * Allocates 32 bytes on the heap but copies an unbounded input . * Overwrites heap metadata and adjacent allocations . */ void vuln_heap_overflow ( const char * input ) { char * buf = malloc (32) ; if (! buf ) return ; strcpy ( buf , input ); printf ("␣␣[ B2 ]␣ Heap ␣ overflow ␣ processed ␣% zu ␣ bytes \n" , strlen ( buf )); free ( buf ); }
175 176 177 178 179 180 181 182 183 184 185 186
/* * B3 — CWE -134: Format String Vulnerability * * User input is passed directly as the format string to printf . * Attacker can read stack memory with %x and write arbitrary * addresses with %n. */ void vuln_format_string ( const char * input ) { printf ("␣␣[ B3 ]␣ Format ␣ string :␣"); printf ( input ); printf ("\n");
Manuscript submitted to ACM
Vulnerability Abundance: A formal proof of infinite vulnerabilities in code
187
21
}
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
/* * B4 — CWE -190: Integer Overflow / Wraparound * * count * size is computed in signed int arithmetic . If the product * exceeds INT_MAX , it wraps to a small ( or negative ) value , causing * malloc to allocate too little memory . The subsequent memset then * writes out of bounds . */ void vuln_integer_overflow ( int count , int size ) { int total = count * size ; char * buf = malloc (( size_t ) total ); if ( buf ) { memset ( buf , 'A ', ( size_t ) count * ( size_t ) size ); printf ("␣␣[ B4 ]␣ Integer ␣ overflow :␣ allocated ␣%d ,␣ wrote ␣% zu \n" , total , ( size_t ) count * ( size_t ) size ); free ( buf ); } }
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
/* * B5 — CWE -416: Use After Free * * Memory is freed and then immediately read . If the allocator has * reused the region , this reads unrelated data . An attacker who * controls the intervening allocation controls the " dangling " read . */ void vuln_use_after_free ( void ) { char * data = malloc (128) ; if (! data ) return ; strcpy ( data , " sensitive_credentials "); free ( data ); printf ("␣␣[ B5 ]␣ Use - after - free :␣%s\n" , data ); }
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
/* * B6 — CWE -415: Double Free * * Freeing the same pointer twice corrupts the allocator 's internal * free - list . An attacker can exploit this to gain arbitrary write . */ void vuln_double_free ( void ) { char * ptr = malloc (64) ; if (! ptr ) return ; strcpy ( ptr , " data "); free ( ptr ); printf ("␣␣[ B6 ]␣ Double ␣ free ␣ triggered \n"); free ( ptr ); }
237 238 239 240 241 242 243 244 245 246 247 248 249
/* * B7 — CWE -78: OS Command Injection * * User input is interpolated into a shell command without * sanitization . Attacker input : "; rm -rf /" or "$( malicious )" */ void vuln_command_injection ( const char * filename ) { char cmd [256]; snprintf ( cmd , sizeof ( cmd ) , " cat ␣%s" , filename ); printf ("␣␣[ B7 ]␣ Command ␣ injection :␣ executing ␣ '%s '\n" , cmd ); system ( cmd ); }
Manuscript submitted to ACM
22
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
/* * B8 — CWE -367: Time -of - Check Time -of - Use ( TOCTOU ) * * The access () check and the fopen () use are non - atomic . Between * the two calls , an attacker can replace the file with a symlink * to a sensitive target (e.g., / etc / shadow ). */ void vuln_toctou ( const char * filepath ) { if ( access ( filepath , R_OK ) == 0) { /* TOCTOU window : file can be swapped between check and use */ FILE *f = fopen ( filepath , "r"); if (f) { char buf [1024]; size_t n = fread ( buf , 1, sizeof ( buf ) - 1, f); buf [n] = '\0 '; printf ("␣␣[ B8 ]␣ TOCTOU ␣ read ␣% zu ␣ bytes \n" , n); fclose (f); } } }
271 272 273 274 275 276 277 278 279 280 281 282 283
/* * B9 — CWE -476: NULL Pointer Dereference * * malloc 's return value is not checked . If allocation fails * (e.g., size == ( size_t ) -1) , the program dereferences NULL . */ void vuln_null_deref ( size_t size ) { char * buf = malloc ( size ); buf [0] = 'A '; printf ("␣␣[ B9 ]␣ NULL ␣ deref :␣ wrote ␣ to ␣ allocation ␣ of ␣ size ␣% zu \n" , size ); free ( buf ); }
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
/* * B10 — CWE -457: Use of Uninitialized Variable * * The stack buffer ' secret ' is only zeroed if flag > 100. Otherwise , * it contains residual data from previous stack frames , which may * include pointers , canaries , or other sensitive values . */ void vuln_uninitialized ( int flag ) { char secret [128]; if ( flag > 100) { memset ( secret , 0, sizeof ( secret )); } printf ("␣␣[ B10 ]␣ Uninitialized :␣ first ␣ byte ␣=␣0x %02 x\n" , ( unsigned char ) secret [0]) ; }
300 301 302 303 304 305 306 307 308 309 310 311 312
/* * B11 — CWE -22: Path Traversal * * User - supplied path component is concatenated without sanitization . * Input "../../ etc / passwd " escapes the intended / var / data / prefix . */ void vuln_path_traversal ( const char * userpath ) { char fullpath [512]; snprintf ( fullpath , sizeof ( fullpath ) , "/ var / data /% s" , userpath ); printf ("␣␣[ B11 ]␣ Path ␣ traversal :␣ would ␣ open ␣ '%s '\n" , fullpath ); /* In a real scenario , fopen ( fullpath , "r ") here */ }
Manuscript submitted to ACM
E. Leverett and J. van der Ham-de Vos
Vulnerability Abundance: A formal proof of infinite vulnerabilities in code
23
313 314 315 316 317 318 319 320
/* * * * * * *
================================================================ SECTION 2: VULNERABILITY GENERATOR This is the mechanism that makes the vulnerability count infinite . Each call generates a new C source file containing 5 distinct vulnerabilities , all parameterized by iteration number N. ================================================================ */
321 322 323 324 325 326 327 328 329
static int read_counter ( void ) { FILE *f = fopen ( COUNTER_FILE , "r"); if (! f) return 0; int n = 0; if ( fscanf (f , "%d" , &n) != 1) n = 0; fclose (f); return n; }
330 331 332 333 334 335 336
static void write_counter ( int n) { FILE *f = fopen ( COUNTER_FILE , "w"); if (! f) { perror (" write_counter "); return ; } fprintf (f , "%d\n" , n); fclose (f); }
337 338 339
static void generate_module ( int n) { char srcpath [512] , libpath [512] , cmd [1024];
340 341 342 343 344
snprintf ( srcpath , sizeof ( srcpath ) , "%s/ vuln_module_ %d.c" , MODULES_DIR , n); snprintf ( libpath , sizeof ( libpath ) , "%s/ vuln_module_ %d%s" , MODULES_DIR , n , SHLIB_EXT );
345 346 347 348 349 350
FILE *f = fopen ( srcpath , "w"); if (! f) { perror (" generate_module :␣ fopen "); return ; }
351 352 353
int buf_size = 16 + n; int alloc_size = 8 + n;
/* Unique stack buffer size */ /* Unique heap allocation size */
354 355 356 357 358 359 360 361 362 363 364
/* --- File header --- */ fprintf (f , " /*\ n"); fprintf (f , "␣*␣ vuln_module_ %d.c␣ — ␣ Auto - generated ␣ vulnerable ␣ module \n" , n); fprintf (f , "␣*␣ Contains ␣5␣ distinct ␣ CVE - class ␣ vulnerabilities ,\ n"); fprintf (f , "␣*␣ each ␣ parameterized ␣ by ␣ iteration ␣N =% d .\ n" , n); fprintf (f , "␣ */\ n"); fprintf (f , "# include ␣ < stdio .h >\ n"); fprintf (f , "# include ␣ < stdlib .h >\ n"); fprintf (f , "# include ␣ < string .h >\ n"); fprintf (f , "# include ␣ < limits .h >\ n\n");
365 366 367 368 369 370 371 372 373 374 375
/* --- Vuln 1: CWE -121 Stack Buffer Overflow --- */ fprintf (f , " /*\ n"); fprintf (f , "␣*␣V (%d ,1) ␣ — ␣ CWE -121: ␣ Stack ␣ Buffer ␣ Overflow \n" , n); fprintf (f , "␣*␣ Buffer ␣ size :␣%d␣ bytes ␣( unique ␣ to ␣ this ␣ module ) .\ n" , buf_size ); fprintf (f , "␣*␣ Exploit ␣ payload ␣ must ␣ be ␣ >=␣%d␣ bytes ␣ to ␣ reach ␣ return ␣ addr .\ n" , buf_size + 1) ; fprintf (f , "␣ */\ n"); fprintf (f , " void ␣ vuln_ % d_overflow ( const ␣ char ␣* input )␣ {\ n" , n); fprintf (f , "␣␣␣␣ char ␣ buf [% d ];\ n" , buf_size ); fprintf (f , "␣␣␣␣ strcpy ( buf ,␣ input ) ;\ n");
Manuscript submitted to ACM
24
376
E. Leverett and J. van der Ham-de Vos fprintf (f , " }\ n\n");
377 378 379 380 381 382 383 384 385
/* --- Vuln 2: CWE -134 Format String --- */ fprintf (f , " /*\ n"); fprintf (f , "␣*␣V (%d ,2) ␣ — ␣ CWE -134: ␣ Format ␣ String ␣ Vulnerability \n" , n); fprintf (f , "␣*␣ User ␣ input ␣ used ␣ directly ␣ as ␣ printf ␣ format ␣ string .\ n"); fprintf (f , "␣ */\ n"); fprintf (f , " void ␣ vuln_ % d_fmtstr ( const ␣ char ␣* input )␣ {\ n" , n); fprintf (f , "␣␣␣␣ printf ( input ) ;\ n"); fprintf (f , " }\ n\n");
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
/* --- Vuln 3: CWE -190 Integer Overflow --- */ fprintf (f , " /*\ n"); fprintf (f , "␣*␣V (%d ,3) ␣ — ␣ CWE -190: ␣ Integer ␣ Overflow \n" , n); fprintf (f , "␣*␣ Overflow ␣ threshold :␣ INT_MAX ␣ -␣%d .\ n" , n); fprintf (f , "␣*␣ Any ␣ user_val ␣ >␣%d␣ causes ␣ signed ␣ overflow .\ n" , n); fprintf (f , "␣ */\ n"); fprintf (f , " void ␣ vuln_ % d_intovf ( int ␣ user_val )␣ {\ n" , n); fprintf (f , "␣␣␣␣ int ␣ base ␣=␣ INT_MAX ␣ -␣%d ;\ n" , n); fprintf (f , "␣␣␣␣ int ␣ total ␣=␣ base ␣+␣ user_val ;\ n"); fprintf (f , "␣␣␣␣ char ␣*p␣=␣ malloc (( size_t ) total ) ;\ n"); fprintf (f , "␣␣␣␣ if ␣(p)␣ {\ n"); fprintf (f , "␣␣␣␣␣␣␣␣ memset (p ,␣ 'A ',␣( size_t )( INT_MAX ␣ -␣%d)␣+␣( size_t ) user_val ) ;\ n" , n); fprintf (f , "␣␣␣␣␣␣␣␣ free (p) ;\ n"); fprintf (f , "␣␣␣␣ }\ n"); fprintf (f , " }\ n\n");
402 403 404 405 406 407 408 409 410 411 412 413 414
/* --- Vuln 4: CWE -416 Use After Free --- */ fprintf (f , " /*\ n"); fprintf (f , "␣*␣V (%d ,4) ␣ — ␣ CWE -416: ␣ Use ␣ After ␣ Free \n" , n); fprintf (f , "␣*␣ Allocation ␣ size :␣%d␣ bytes ␣( unique ␣ heap ␣ layout ) .\ n" , alloc_size ); fprintf (f , "␣ */\ n"); fprintf (f , " void ␣ vuln_ % d_uaf ( void )␣ {\ n" , n); fprintf (f , "␣␣␣␣ char ␣*p␣=␣ malloc (% d) ;\ n" , alloc_size ); fprintf (f , "␣␣␣␣ if ␣ (! p)␣ return ;\ n"); fprintf (f , "␣␣␣␣ strcpy (p ,␣ \" secret \") ;\ n"); fprintf (f , "␣␣␣␣ free (p) ;\ n"); fprintf (f , "␣␣␣␣ printf (\"%% s \\ n \" , ␣p) ;\ n"); fprintf (f , " }\ n\n");
415 416 417 418 419 420 421 422 423 424 425
/* --- Vuln 5: CWE -78 Command Injection --- */ fprintf (f , " /*\ n"); fprintf (f , "␣*␣V (%d ,5) ␣ — ␣ CWE -78: ␣ OS ␣ Command ␣ Injection \n" , n); fprintf (f , "␣*␣ Unsanitized ␣ user ␣ input ␣ passed ␣ to ␣ system () .\ n"); fprintf (f , "␣ */\ n"); fprintf (f , " void ␣ vuln_ % d_cmdinj ( const ␣ char ␣* input )␣ {\ n" , n); fprintf (f , "␣␣␣␣ char ␣ cmd [512];\ n"); fprintf (f , "␣␣␣␣ snprintf ( cmd ,␣ sizeof ( cmd ) ,␣ \" echo ␣ module_ %d:␣ %% s \" , ␣ input ) ;\ n" , n); fprintf (f , "␣␣␣␣ system ( cmd ) ;\ n"); fprintf (f , " }\ n\n");
426 427 428 429 430 431 432 433 434 435
/* --- Module entry point --- */ fprintf (f , " /* ␣ Entry ␣ point ␣ invoked ␣ by ␣ the ␣ factory ␣ loader ␣ */\ n"); fprintf (f , " void ␣ module_entry ( const ␣ char ␣* input )␣ {\ n"); fprintf (f , "␣␣␣␣ vuln_ % d_overflow ( input ) ;\ n" , n); fprintf (f , "␣␣␣␣ vuln_ % d_fmtstr ( input ) ;\ n" , n); fprintf (f , "␣␣␣␣ vuln_ % d_intovf (42) ;\ n" , n); fprintf (f , "␣␣␣␣ vuln_ % d_uaf () ;\ n" , n); fprintf (f , "␣␣␣␣ vuln_ % d_cmdinj ( input ) ;\ n" , n); fprintf (f , " }\ n");
436 437
fclose (f);
438
Manuscript submitted to ACM
Vulnerability Abundance: A formal proof of infinite vulnerabilities in code
25
/* Compile the module into a shared library */ snprintf ( cmd , sizeof ( cmd ) , COMPILE_SHLIB_FMT , libpath , srcpath ); if ( system ( cmd ) != 0) { fprintf ( stderr , " [!] ␣ Compilation ␣ of ␣ module ␣%d␣ failed \n" , n); return ; }
439 440 441 442 443 444 445
printf (" [+] ␣ Generated ␣ vuln_module_ %d:␣5␣ new ␣ vulnerabilities ␣" "( buf =%d ,␣ alloc =%d ,␣ overflow_at = INT_MAX -% d)\n" , n , buf_size , alloc_size , n);
446 447 448 449
}
450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
/* * * * * * * * * * * * * *
================================================================ SECTION 3: MODULE LOADER Scans the modules directory for compiled shared libraries and dynamically loads each one . Additional vulnerability : CWE -426 ( Untrusted Search Path ) — we load . so /. dylib files from a directory with 0777 permissions that could be writable by an attacker . CWE -401 ( Memory Leak ) — dlopen handles are intentionally never closed , leaking resources on every invocation . ================================================================ */
465 466 467 468
static int load_and_run_modules ( const char * input ) { DIR * dir = opendir ( MODULES_DIR ); if (! dir ) return 0;
469
struct dirent * entry ; int count = 0;
470 471 472
while (( entry = readdir ( dir )) != NULL ) { char * ext = strrchr ( entry -> d_name , '. '); if (! ext || strcmp ( ext , SHLIB_EXT ) != 0) continue ;
473 474 475 476 477
char path [512]; snprintf ( path , sizeof ( path ) , "%s /% s" , MODULES_DIR , entry -> d_name );
478 479 480
void * handle = dlopen ( path , RTLD_LAZY ); if (! handle ) { fprintf ( stderr , " [!] ␣ dlopen (% s):␣%s\n" , path , dlerror () ); continue ; }
481 482 483 484 485 486
typedef void (* entry_fn )( const char *) ; entry_fn mod_entry = ( entry_fn ) dlsym ( handle , " module_entry "); if ( mod_entry ) { printf (" [*] ␣ Running ␣%s␣ ...\ n" , entry -> d_name ); mod_entry ( input ); count ++; }
487 488 489 490 491 492 493 494
/* Intentional : never calling dlclose ( handle )
495
—
CWE -401 */
}
496 497
closedir ( dir ); return count ;
498 499 500
}
501
Manuscript submitted to ACM
26
502 503 504
E. Leverett and J. van der Ham-de Vos
/* ================================================================ * SECTION 4: MAIN PROGRAM * ================================================================ */
505 506 507 508 509 510 511
static void print_banner ( void ) { printf (" =====================================================\ n"); printf ("␣␣␣␣ The ␣ Infinite ␣ Vulnerability ␣ Factory ␣ v1 .0\ n"); printf ("␣␣␣␣ EDUCATIONAL ␣ PURPOSE ␣ ONLY ␣ — ␣ DO ␣ NOT ␣ DEPLOY \n"); printf (" =====================================================\ n"); }
512 513 514 515 516 517 518 519 520 521
static void print_menu ( void ) { printf ("\ nMenu :\ n"); printf ("␣␣ 1) ␣ Trigger ␣ base ␣ vulnerabilities ␣ with ␣ input \n"); printf ("␣␣ 2) ␣ Generate ␣a␣ new ␣ vulnerable ␣ module \n"); printf ("␣␣ 3) ␣ Load ␣ and ␣ run ␣ all ␣ generated ␣ modules \n"); printf ("␣␣ 4) ␣ Show ␣ vulnerability ␣ census ␣ and ␣ proof \n"); printf ("␣␣ 5) ␣ Exit \n"); printf (" >␣"); }
522 523 524 525 526 527
static void show_census ( void ) { int k = read_counter () ; int base = 11; int generated = k * 5; int total = base + generated ;
528
printf ("\n ---␣ Vulnerability ␣ Census ␣ ---\n"); printf (" Base ␣ vulnerabilities ␣( in ␣ vuln_factory ␣ itself ):␣␣%d\n" , base ); printf ("␣␣ B1 :␣␣ CWE -121 ␣␣ Stack ␣ Buffer ␣ Overflow \n"); printf ("␣␣ B2 :␣␣ CWE -122 ␣␣ Heap ␣ Buffer ␣ Overflow \n"); printf ("␣␣ B3 :␣␣ CWE -134 ␣␣ Format ␣ String \n"); printf ("␣␣ B4 :␣␣ CWE -190 ␣␣ Integer ␣ Overflow \n"); printf ("␣␣ B5 :␣␣ CWE -416 ␣␣ Use ␣ After ␣ Free \n"); printf ("␣␣ B6 :␣␣ CWE -415 ␣␣ Double ␣ Free \n"); printf ("␣␣ B7 :␣␣ CWE -78 ␣␣␣ OS ␣ Command ␣ Injection \n"); printf ("␣␣ B8 :␣␣ CWE -367 ␣␣ TOCTOU ␣ Race ␣ Condition \n"); printf ("␣␣ B9 :␣␣ CWE -476 ␣␣ NULL ␣ Pointer ␣ Dereference \n"); printf ("␣␣ B10 :␣ CWE -457 ␣␣ Uninitialized ␣ Variable \n"); printf ("␣␣ B11 :␣ CWE -22 ␣␣␣ Path ␣ Traversal \n"); printf ("\n"); printf (" Generated ␣ modules :␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣%d\n" , k); printf (" Vulnerabilities ␣ per ␣ module :␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣ 5\ n"); printf ("␣␣ Per ␣ module :␣ CWE -121 , ␣ CWE -134 , ␣ CWE -190 , ␣ CWE -416 , ␣ CWE -78\ n"); printf (" Total ␣ generated ␣ vulnerabilities :␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣%d\n" , generated ); printf ("\n"); printf (" === ␣ GRAND ␣ TOTAL :␣%d␣ distinct ␣ CVE - class ␣ vulns ␣ ===\ n\n" , total ); printf (" Growth ␣ formula :␣T(k)␣=␣ 11 ␣+␣5k ,␣ where ␣k␣=␣ number ␣ of ␣ runs \n"); printf ("␣␣T (0) ␣␣␣=␣ 11\ n"); printf ("␣␣T (10) ␣␣=␣ 61\ n"); printf ("␣␣T (100) ␣=␣ 511\ n"); printf ("␣␣ lim (k␣ ->␣ inf )␣T(k)␣=␣ infinity \n"); printf ("\ nEach ␣ vulnerability ␣ is ␣ independently ␣ patchable ␣ and \n"); printf (" resides ␣ in ␣a␣ distinct ␣ software ␣ component ,␣ satisfying \n"); printf (" CVE ␣ assignment ␣ criteria ␣ for ␣ separate ␣ identifiers .\ n");
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
}
558 559 560
int main ( int argc , char * argv []) { print_banner () ;
561 562 563
/* CWE -732: Insecure default permissions */ mkdir ( MODULES_DIR , 0777) ;
564
Manuscript submitted to ACM
Vulnerability Abundance: A formal proof of infinite vulnerabilities in code
565 566 567 568 569 570
27
/* Auto - generate one new module on each execution */ int iteration = read_counter () ; printf ("\n [+] ␣ Iteration ␣%d:␣ auto - generating ␣ new ␣ vulnerable ␣ module ...\ n" , iteration ); generate_module ( iteration ); write_counter ( iteration + 1) ;
571 572 573
printf (" [+] ␣ This ␣ program ␣ now ␣ contains ␣%d␣ known ␣ vulnerabilities .\ n" , 11 + ( iteration + 1) * 5) ;
574 575 576
char input [ INPUT_BUFSZ ]; int running = 1;
577 578 579 580 581
while ( running ) { print_menu () ; if (! fgets ( input , sizeof ( input ) , stdin )) break ; input [ strcspn ( input , "\n")] = '\0 ';
582 583 584 585 586 587
switch ( atoi ( input )) { case 1: printf (" Enter ␣ input ␣ string :␣"); if (! fgets ( input , sizeof ( input ) , stdin )) break ; input [ strcspn ( input , "\n")] = '\0 ';
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
printf ("\n ---␣ Base ␣ Vulnerabilities ␣(B1 - B11 )␣ ---\n"); vuln_stack_overflow ( input ); vuln_heap_overflow ( input ); vuln_format_string ( input ); vuln_integer_overflow ( atoi ( input ) , 1000) ; vuln_use_after_free () ; /* NOTE : double_free will likely crash the process */ /* vuln_double_free () ; */ printf ("␣␣[ B6 ]␣ Double ␣ free :␣ skipped ␣( would ␣ crash );␣ see ␣ source \n"); vuln_command_injection ( input ); vuln_toctou ( input ); /* NOTE : null_deref will likely crash the process */ /* vuln_null_deref (( size_t ) -1) ; */ printf ("␣␣[ B9 ]␣ NULL ␣ deref :␣ skipped ␣( would ␣ crash );␣ see ␣ source \n"); vuln_uninitialized ( atoi ( input )); vuln_path_traversal ( input ); printf (" ---␣ Done ␣ (11 ␣ base ␣ vulns ␣ triggered )␣ ---\n"); break ;
607 608 609 610 611 612 613 614 615
case 2: { int cur = read_counter () ; generate_module ( cur ); write_counter ( cur + 1) ; printf (" [+] ␣ Now ␣ at ␣%d␣ total ␣ vulnerabilities .\ n" , 11 + ( cur + 1) * 5) ; break ; }
616 617 618 619 620 621 622 623 624 625 626
case 3: printf (" Enter ␣ input ␣ for ␣ modules :␣"); if (! fgets ( input , sizeof ( input ) , stdin )) break ; input [ strcspn ( input , "\n")] = '\0 '; { int n = load_and_run_modules ( input ); printf (" [+] ␣ Executed ␣%d␣ modules ␣ (% d␣ generated ␣ vulns )\n" , n , n * 5) ; } break ;
627
Manuscript submitted to ACM
28
E. Leverett and J. van der Ham-de Vos case 4: show_census () ; break ;
628 629 630 631
case 5: running = 0; break ;
632 633 634 635
default : printf (" Invalid ␣ choice .\ n"); break ; }
636 637 638 639
}
640 641
printf ("\ nGoodbye .␣ Remember :␣ every ␣ run ␣ created ␣ more ␣ vulnerabilities .\ n"); return 0;
642 643 644
}
Listing 2. vuln_factory.c — The Infinite Vulnerability Factory (622 lines). Educational purpose only. Released under the MIT Licence.
Manuscript submitted to ACM