Finding a billion-user project for Eiffel: How DbC catches the security flaws that Rust misses

by Finnian Reilly (modified: 2026 May 22)

Preface

This essay was developed in collaboration with Claude. The technical observations and strategic judgements are my own, drawn from decades of Eiffel development. The AI contributed structure, prose, and breadth of reference across the security landscape.

Introduction: Eiffel's visibility problem

Eiffel is one of the most carefully designed programming languages in existence. Its type system eliminates null pointer dereferences statically. Its Design by Contract mechanism allows developers to express and verify correctness properties directly in source code. Its compile-to-C backend produces portable, auditable output that works with any C compiler on any platform. Its garbage collector can be selectively disabled for performance-critical sections. It has been battle-tested for decades across finance, aerospace, and enterprise software.

And almost nobody outside those circles has heard of it.

This is not a new observation. The Eiffel community has been aware of this visibility gap for years. Various explanations have been offered — the commercial history of EiffelStudio, the academic associations, the relatively small developer pool, the absence of a breakout open source project that non-Eiffel developers encounter in their daily work.

This essay argues that the conditions are now better than they have ever been for Eiffel to change that situation — not through marketing, not through language advocacy, but through building one specific thing that the world genuinely needs and that Eiffel is uniquely qualified to provide.

That thing is a streaming XML parser with a libexpat-compatible interface, written in Eiffel, with full Design by Contract annotations, called xpact.

To understand why this matters, we need to understand what is happening in the broader software security landscape right now.


The memory safety moment

Something significant has shifted in how governments and major technology organisations talk about software security. For decades the security conversation focused on processes — patch management, vulnerability disclosure, penetration testing, secure development guidelines. The underlying assumption was that insecure code was an unfortunate but inevitable consequence of the difficulty of programming, and the best you could do was find and fix vulnerabilities quickly.

That assumption is now being challenged at the highest levels.

In 2022 the US National Security Agency published guidance explicitly recommending that organisations migrate away from C and C++ toward memory-safe languages. In 2023 the White House Office of the National Cyber Director published a report calling on the software industry to eliminate entire classes of vulnerability through language choice rather than reactive patching. The same year CISA — the Cybersecurity and Infrastructure Security Agency — published a joint advisory with international partners echoing the same message.

The specific concern is a class of bugs that have plagued C and C++ programs since their inception: buffer overflows, use-after-free errors, double-free errors, null pointer dereferences, and related memory corruption vulnerabilities. These bugs arise from the fact that C gives programmers direct control over memory with no runtime safety net. Get it wrong and the consequences range from crashes to complete system compromise. Decades of security research has shown that this class of bug accounts for a disproportionate fraction of critical security vulnerabilities — Microsoft has stated that approximately 70% of their security vulnerabilities over a period of years were memory safety issues. Google has reported similar figures for Chrome.

The proposed solution is to replace C and C++ with languages that eliminate memory safety bugs by construction — languages where the type system or runtime makes the entire class of error impossible. Rust has emerged as the industry's favoured answer to this challenge, and it is worth understanding exactly what Rust does well before examining what it cannot do.


What is a CVE and why should Eiffel developers care?

A CVE — Common Vulnerabilities and Exposures — is a standardised identifier assigned to a publicly disclosed security vulnerability. Each CVE has the format CVE-YEAR-NUMBER, for example CVE-2024-8176. The year indicates when the identifier was assigned, not necessarily when the vulnerability was introduced or discovered. The system is maintained by MITRE Corporation and sponsored by the US Department of Homeland Security.

When a vulnerability is discovered and responsibly disclosed, it receives a CVE identifier along with a description of the flaw, a CVSS score (a number from 0 to 10 indicating severity), and references to affected versions and available patches. Security scanning tools used by enterprises, cloud providers, and Linux distributions all track CVE identifiers to flag vulnerable software automatically.

For a library developer, a CVE is simultaneously a technical failure and a reputational event. Each CVE represents a bug that reached production, was discovered by a researcher or attacker, and required an emergency patch and coordinated disclosure across every downstream project that embeds the library. For widely-embedded libraries this process affects hundreds of projects simultaneously.

Why should Eiffel developers care about CVEs? Because a new Eiffel library that can credibly claim a lower CVE rate than its C equivalent — and more importantly explain architecturally *why* that is the case — has a compelling story to tell to exactly the audience that the memory safety moment has made receptive. Security teams, procurement processes, and engineering leadership at major organisations are actively looking for justification to replace C libraries. A well-argued, well-demonstrated Eiffel alternative is the kind of thing that gets written about on Hacker News, cited in security conference talks, and referenced in procurement decisions.

Zero CVEs at launch is easy to claim. Explaining why the architecture makes entire classes of CVE structurally impossible is the claim worth making — and the one that requires Eiffel's specific capabilities to support.


Enter Rust: the memory safety champion

Rust was created by Graydon Hoare, a programmer at Mozilla, initially as a personal project begun around 2006. The story of its origin is almost poetic: returning home one day to find his apartment building's elevator broken due to a software crash, Hoare was struck by the absurdity of computer people being unable to write elevator software that didn't crash. He knew, as any systems programmer would, that such crashes are frequently caused by memory safety bugs. That frustration seeded years of work that would eventually become Rust.

Mozilla began sponsoring Rust around 2009, recognising that Firefox's enormous C++ codebase was a perpetual source of security vulnerabilities. They funded both the language itself and Servo — an experimental browser engine written entirely in Rust that served as a proving ground for the language's capabilities. In 2021 the independent Rust Foundation was established with backing from Google, Microsoft, Amazon, and others, cementing Rust's position as an industry-supported language rather than a Mozilla project.

Rust's core innovation is the borrow checker — a compile-time analysis that enforces strict rules about ownership and borrowing of memory. The rules ensure that memory is always either owned by exactly one binding or borrowed under controlled conditions, making it impossible to have two mutable references to the same data simultaneously, impossible to use memory after it has been freed, and impossible to free memory twice. These guarantees are checked at compile time with no runtime overhead — the resulting binary is as fast as equivalent C code while being provably free of the specific class of memory corruption bugs that the checker addresses.

The results have been significant. Stylo, Firefox's CSS rendering engine rewritten in Rust, eliminated an entire category of memory corruption CVEs from that component. Android's Rust adoption has correlated with measurable reductions in memory safety vulnerabilities. The Linux kernel began accepting Rust code in version 6.1.

Perhaps most visibly for this discussion, sudo-rs — a Rust rewrite of the sudo utility — shipped as the default sudo implementation in Ubuntu 25.10, released in October 2025, ahead of its anticipated adoption in the Ubuntu 26.04 LTS release expected in April 2026. This would place a Rust-based security-critical utility on tens of millions of systems.

Rust is a genuine achievement and its memory safety guarantees are real. But there is a flaw in the argument that memory safety is sufficient for security — and it showed up almost immediately in sudo-rs.


The flaw in the memory safety argument

In July 2025, shortly after sudo-rs began shipping in Ubuntu, researchers discovered two vulnerabilities in the implementation. Neither was a memory safety violation. Both were logic errors.

One allowed a user with limited sudo privileges to enumerate other users' sudo permissions — information that should have been inaccessible. The other allowed authentication to be bypassed under specific conditions involving cached credentials. Both vulnerabilities were entirely invisible to Rust's borrow checker, because the borrow checker has no opinion about authentication logic, permission models, or what information one user should be allowed to see about another.

This is not a criticism of Rust specifically. It is an illustration of a fundamental limitation of the memory safety framing. Memory safety addresses one class of bug — the class arising from incorrect manual memory management. It says nothing about whether your authentication logic is correct, whether your permission checks are complete, whether your state machine can reach an invalid state, or whether a crafted input can drive your algorithm into quadratic time complexity.

Consider the CVE history of libexpat, the widely-deployed C XML parser:

  • CVE-2024-8176 — a stack overflow triggered by deeply nested entity references, causing a crash through recursive exhaustion
  • CVE-2025-59375 — insufficient controls on dynamic memory allocation, allowing a small crafted XML document to trigger massive heap allocation
  • CVE-2025-66382 — a crafted 2MB XML file causing dozens of seconds of CPU time through algorithmic complexity
  • Multiple integer overflow CVEs from 2024 — negative length values accepted without validation, integer wraparound on 32-bit platforms

Look carefully at this list. The stack overflow is caused by absent recursion depth checking — a missing precondition. The unbounded memory allocation is caused by absent allocation limits — a missing invariant. The algorithmic complexity attack is caused by absent input complexity bounds — a missing precondition. The integer overflows are caused by absent range validation — missing preconditions.

Every single one of these CVEs is a logic error expressible as a Design by Contract annotation. Every single one would have been caught during development if the code had been written with explicit preconditions and invariants. None of them is a memory corruption bug. None of them would have been prevented by rewriting libexpat in Rust.

This is the flaw in the memory safety argument: it addresses the implementation language's failure modes, not the algorithm's failure modes. A Rust rewrite of libexpat would eliminate use-after-free and buffer overflows — which libexpat has also had — but would do nothing for the logic errors that comprise the majority of its recent CVE history.

Design by Contract addresses logic errors directly. That is the gap Eiffel can fill.


Design by Contract: the missing piece

Design by Contract was introduced by Bertrand Meyer as a central feature of the Eiffel programming language in the late 1980s and formally described in his book Object-Oriented Software Construction. The core idea is straightforward but profound: every routine in a program has a contract with its callers, consisting of three parts.

A precondition expresses what must be true when the routine is called — the caller's obligation. A postcondition expresses what will be true when the routine returns — the routine's guarantee. A class invariant expresses what must always be true about a valid object of that class — the consistency guarantee that every routine must maintain.

In Eiffel these are not comments or documentation. They are executable code, evaluated at runtime during development and testing. A precondition violation means the caller broke the contract. A postcondition violation means the routine failed to deliver its guarantee. An invariant violation means an object has reached an inconsistent state. Each violation is a precisely located, automatically detected bug.

Consider how DbC would express the constraints that libexpat's CVE history reveals were missing:

parse_entity_reference (depth: INTEGER) require depth_within_bounds: depth <= Maximum_entity_depth ... ensure stack_within_limits: current_stack_depth <= Maximum_stack_depth end allocate_buffer (requested_size: INTEGER) require size_is_positive: requested_size > 0 size_within_amplification_limit: requested_size <= input_size * Maximum_amplification_factor ... parse_chunk (buffer: SPECIAL [CHARACTER_8]; length: INTEGER) require length_non_negative: length >= 0 length_within_buffer: length <= buffer.count ...

Each of these preconditions is a one-liner that directly corresponds to a class of CVE that libexpat has shipped. They are not afterthoughts — they are the specification. Writing them forces the developer to think about the contract at the moment of implementation, before any test is written, before any fuzzer runs, before any deployment.

During development and testing, every violation of every contract is automatically detected and reported with a precise location and the values that triggered the violation. The XML specification itself defines precise rules about valid documents, valid entity expansion ratios, valid nesting depths, and valid encoding sequences — all of which map directly to preconditions and invariants. An Eiffel XML parser is a natural home for a machine-readable encoding of the XML specification.

Eiffel also supports loop invariants and loop variants — formal correctness proofs embedded directly in loop constructs. The loop invariant expresses what remains true on every iteration; the loop variant is an integer expression proven to decrease on every iteration and remain non-negative, formally guaranteeing termination. For a parser like xpact whose hot paths consist largely of byte-scanning loops, these are not academic curiosities — they are the mechanism by which the correctness of each scanning loop is formally established rather than assumed.

The crucial point is that contracts do their most valuable work during development even though they may be disabled in the production build. By the time you ship, every test run, every fuzz input, every regression test has been checked against every contract. The logic errors that became libexpat CVEs would have surfaced as contract violations during testing — not as crash reports from production systems.


Choosing the right target: criteria for a billion-user Eiffel project

Not every C library is a good candidate for an Eiffel reimplementation intended to raise Eiffel's profile. The ideal candidate has several specific properties.

Widely deployed — the library should be present on a very large number of systems, ideally in the hundreds of millions. This is what gives the "billion-user" claim its meaning. A library used by Python, by Mozilla, by hundreds of Linux distributions, by embedded systems, by enterprise software stacks is a library whose replacement would be noticed.

Clear CVE history — the library should have a documented track record of security vulnerabilities, specifically of the logic error variety that DbC addresses rather than exclusively memory corruption bugs. This is what makes the "catches the flaws that Rust misses" argument concrete and falsifiable rather than theoretical.

No serious Rust rewrite underway — the project needs a clear field to operate in. If a well-funded Rust replacement already exists and is shipping, the strategic window has closed. The goal is to arrive first, not to compete with an entrenched alternative.

Achievable scope — the library should be small enough that a focused team of Eiffel developers can produce a credible implementation in a reasonable timeframe. A project that requires five years and twenty developers before it can be evaluated publicly is not a visibility win — it is a long-term bet that may never pay off.

Domain where DbC is visibly valuable — the library's domain should be one where the correctness properties are well-defined and expressible as contracts. Parsers and protocol implementations are ideal because they implement formal specifications — the XML specification, the HTTP specification, the DNS specification — where preconditions and postconditions can be derived directly from the standard.

Permissive licence — the original C library should be under a licence that permits a clean-room reimplementation without legal complications. MIT and BSD licences are ideal.

libexpat satisfies every one of these criteria.


Why libexpat is the ideal candidate

libexpat is a stream-oriented XML parser written in C, originally created by James Clark — the technical lead of the W3C XML Working Group that produced the XML specification itself. Clark named it after his own status as a British expatriate living in Thailand, with the secondary meaning of XML Parser Toolkit. He released it in 1998 under what is now the MIT licence, made it the parser used to add XML support to Netscape 5, and handed it to a maintenance team around 2000.

It is embedded in an extraordinary range of software. Python's standard library uses it as the backend for xml.parsers.expat and the higher-level xml.etree and xml.sax modules — meaning it is present on every Python installation on every platform. It was used by Mozilla. It is a dependency of hundreds of Linux packages. It appears in embedded systems and hardware devices. Conservative estimates of the number of systems carrying libexpat run into the hundreds of millions.

Its CVE history is extensive and ongoing. The list includes integer overflows, stack overflows from recursive entity expansion, unbounded memory allocation, negative length acceptance, algorithmic complexity attacks, and null pointer dereferences — with new CVEs appearing as recently as early 2026. Critically, the recent CVEs are predominantly logic errors rather than memory corruption bugs, which means a Rust rewrite would not have prevented them.

The library is small. The core implementation lives in approximately five source files — xmlparse.c (around 7,000-8,000 lines), xmltok.c with its multiply-included encoding variants, xmlrole.c (the prolog state machine), and supporting headers. The entire codebase is perhaps 12,000-14,000 lines of C. In Eiffel, with generics handling the encoding variants that C addresses through macro-based multiple inclusion, and with the state machine mapping naturally to a class hierarchy, a complete implementation would likely be around 4,000-6,000 lines — achievable by a small focused team.

libexpat is entirely single-threaded. There is no internal concurrency, no mutex, no shared state between parser instances. The thread safety model is simply one parser instance per thread. This simplifies the implementation considerably and, as we will see, opens interesting opportunities for Eiffel's SCOOP model to add value beyond the baseline.

No serious Rust rewrite of libexpat exists. A mechanical C2Rust transpilation called rexpat exists, maintained by Immunant, with the stated ambition of eventually becoming safe idiomatic Rust — but it remains explicitly marked as a work-in-progress producing unsafe Rust, has no published releases, and is not on crates.io. It is not for production use. The field is open.

The licence is MIT. There are no legal complications.

libexpat's maintainer has publicly sought help from the organisations embedding it, writing to approximately forty companies asking for security contacts and assistance. There is institutional awareness of the problem and an audience already primed to care about a credible replacement.


Introducing xpact

The proposed project is called xpact. The name works on two levels simultaneously: it sounds like expat, immediately signalling to existing developers what it is and what it replaces; and pact means a formal agreement — a direct reference to Design by Contract without spelling it out. The tagline is straightforward: xpact — XML parsing, by contract.

The architecture has several key components.

The core parser is written in Eiffel, implementing the XML 1.0 specification as a streaming parser with the same event-driven callback model as libexpat. The key difference is that every significant operation carries explicit DbC annotations derived directly from the XML specification and from the known constraints that libexpat's CVE history reveals were missing.

The C-compatible interface is a thin layer that exports the libexpat public API — XML_ParserCreate, XML_SetElementHandler, XML_SetCharacterDataHandler, XML_Parse, and the remaining public functions — as C-callable functions. This makes xpact a drop-in replacement: any application currently linking against libexpat can switch to xpact by substituting the library file without changing a single line of application code.

The shared library target uses EiffelStudio's ability to compile Eiffel to a DLL on Windows or SO on Linux and macOS, with the Eiffel runtime bundled within the library. The library presents a standard C ABI to its callers. The Eiffel internals are invisible.

The Python binding provides a PyPI-installable package that wraps xpact for Python use. Given that Python's standard library already uses libexpat as its XML parsing backend, a well-packaged Python binding with clear security documentation would reach Python's enormous developer community directly. A Python developer choosing between the standard library's expat module and xpact with published contract annotations and CVE-regression test results is exactly the kind of choice that generates discussion and adoption.

The name libxpact for the shared library and xpact for the Python package completes the naming scheme.

A .NET parser component. EiffelStudio's .NET backend compiles Eiffel directly to Common Intermediate Language (CIL), producing a proper .NET assembly rather than a native shared library. This means xpact could ship simultaneously as a native libxpact.so / xpact.dll for C and Python consumers, and as a NuGet-installable .NET assembly consumable directly from C#, F#, or VB.NET — all from the same Eiffel source base. For .NET consumers the Eiffel classes appear as native .NET types with no P/Invoke FFI layer required. More importantly, the .NET build exposes xpact's parsing events as native CLR delegates rather than C function pointers, giving C# and F# consumers an idiomatic event-driven API that feels entirely natural within the .NET ecosystem — a considerably more appealing product than a thin wrapper around C conventions would be. The GC interaction concern that applies to the native build disappears entirely in the .NET target, since everything runs inside the CLR's own managed environment. Since the .NET runtime is already present on any .NET-capable system, the runtime bundling overhead of the native build does not arise here either — the CLR simply hosts the assembly alongside everything else. This is a capability Rust cannot easily match, and it significantly widens xpact's potential audience to include the enormous .NET enterprise ecosystem. A single Eiffel codebase therefore delivers three deployment targets — native C-compatible shared library, Python PyPI package, and .NET NuGet package — each reaching a distinct developer community with an API idiomatic to that community.


Eiffel's practical advantages for this specific task

Several specific properties of Eiffel and EiffelStudio make xpact not merely possible but genuinely well-suited to this task in ways that are worth stating explicitly.

Compile-to-C portability. EiffelStudio generates C as an intermediate representation, which is then compiled by whatever C compiler is available on the target platform — GCC, Clang, or MSVC. This means xpact is portable across Windows, Linux, and macOS without platform-specific code, and integrates naturally into build systems that already use those compilers. This is a significant advantage over Rust, which manages its own compilation pipeline and has historically required effort to integrate into Windows MSVC-based projects.

SPECIAL arrays for hot-path performance. The innermost loop of an XML parser — scanning bytes, recognising token boundaries, checking encoding validity — needs to be fast. Eiffel's SPECIAL type is a contiguous block of typed memory with no object overhead, no GC pressure, and cache-friendly sequential access. Operations on SPECIAL arrays generate tight C loops equivalent to what a C programmer would write directly. The hot path of xpact can be SPECIAL-backed throughout, delivering performance competitive with the C original.

GC management during parsing. The garbage collector can be disabled for the duration of a parse and all memory reclaimed in a single operation when the parse completes. A parse has a natural transactional boundary — start, process, finish — that maps perfectly to this model. Intermediate objects (token strings, attribute value buffers, namespace prefix strings) are all short-lived and their lifetime is bounded by the parse. Turning the GC off during parsing means no GC pauses during the critical path and entirely predictable memory behaviour — important for callers embedding xpact in performance-sensitive contexts.

String pool recycling. XML documents are highly repetitive in terms of string vocabulary — the same element names, attribute names, and namespace prefixes appear thousands of times in a large document. A string pool that interns these strings, allocating each unique string once and returning a reference for subsequent occurrences, reduces allocation pressure dramatically. This pattern is used in Eiffel-Loop and is directly applicable to xpact. Combined with GC-off parsing, the memory behaviour becomes competitive with the most optimised C implementations.

Simplified C callbacks without freezing. Normally, passing Eiffel object references into C callback contexts requires "freezing" the objects — pinning them in memory so the GC cannot relocate them during a collection cycle. This adds complexity and is a potential source of bugs if forgotten. With the GC disabled during parsing, objects do not move, and the entire freeze/unfreeze ceremony is unnecessary. The C-compatible callback layer becomes straightforward to implement and impossible to get wrong in this specific way.

SCOOP for pipeline parallelism. libexpat is single-threaded by design. xpact can offer an optional parallel parsing mode using Eiffel's SCOOP concurrency model, implementing a pipeline where the tokenizer, attribute parser, and callback dispatcher run as separate concurrent processors passing work through queues. From the caller's perspective the API is identical — register handlers, call parse, receive callbacks. The parallelism is an internal implementation detail. The correctness of the pipeline is guaranteed by SCOOP's model rather than requiring manual mutex management. This would be a genuine capability improvement over libexpat — the same API, internally pipelined for multi-core hardware.

Void safety. Eiffel's void safety guarantee is static and always active regardless of whether contracts are enabled in the production build. It eliminates null pointer dereference — one of libexpat's historical CVE types — at compile time. No runtime check is needed because the type system proves it cannot occur.

Decades-tested IDE and tooling. EiffelStudio has been refined over decades around a single philosophy — that code should be comprehensible, navigable, and provably correct. Its diagram tool generates live class diagrams directly from source. Its contract browser presents preconditions, postconditions and invariants as first-class navigable elements alongside the code they annotate. The flat and short forms of a class expose the complete inherited interface without manual hierarchy navigation. This level of integration between correctness mechanism and development environment has no equivalent in the Rust ecosystem, where IDE support — while good and improving — is provided through general-purpose plugins rather than an environment built around the language's specific philosophy. For a security-critical library expected to be maintained for years by contributors who may not have written the original code, this difference matters practically. xpact's maintainability story is not just about Eiffel's language properties — it is about the entire environment in which it is developed and evolved.


Running the expat test suite as a correctness proof

libexpat has a comprehensive test suite with greater than 90% code coverage. More importantly for xpact, it includes regression tests for every historical CVE — specific crafted inputs that triggered each past vulnerability, ensuring that fixed bugs do not regress.

The xpact development methodology is straightforward: run the complete libexpat test suite against xpact in development mode with full contract checking enabled. Every precondition, postcondition, and invariant is evaluated on every test input. Contract violations become test failures.

The CVE regression tests are particularly valuable in this context. The crafted input that triggered CVE-2024-8176 — deeply nested entity references designed to exhaust the stack — will, when run against xpact, trigger the recursion depth precondition before any stack exhaustion can occur. The crafted input for CVE-2025-59375 — a small document triggering massive allocation — will trigger the amplification factor precondition. The 2MB document from CVE-2025-66382 designed to cause dozens of seconds of parse time will trigger the complexity bound precondition.

Each historical CVE becomes a contract violation caught in testing rather than a silent failure in production. The claim that xpact can then make is specific and verifiable: xpact passed the complete libexpat test suite including all CVE regression tests with full Design by Contract checking enabled, with zero contract violations. This is an empirical correctness statement, not a theoretical one, backed by the most battle-hardened XML parser test suite in existence.

No C implementation can make an equivalent statement because C has no equivalent mechanism. The closest a C parser can say is "our tests passed" — but the tests do not formally verify the contract between each function and its callers the way Eiffel's contracts do.

The role of fuzzing

The test suite proves correctness for known inputs. Adversarial inputs in the wild are a different challenge. A sufficiently motivated attacker will try inputs that no test author anticipated — crafted encodings, malformed document structures, inputs designed to drive parser state into unexpected combinations.

Fuzzing addresses this. libexpat itself participates in OSS-Fuzz, Google's continuous fuzzing infrastructure, which generates millions of malformed inputs automatically and reports crashes and hangs. xpact should participate in the same infrastructure.

Eiffel's contracts make fuzzing more productive, not less. When a fuzzer finds an input that triggers a contract violation in development mode, it produces a precise, machine-generated report of exactly which invariant was broken and what input caused it — not a crash dump requiring reverse engineering. The feedback loop from fuzzer to fix is correspondingly faster and more informative.


Addressing the honest objections

A credible proposal addresses its weaknesses directly.

Contracts are disabled in the production build.

This is true and important to acknowledge. Finalised Eiffel builds disable contract checking for performance. However, contracts do their most valuable work during development. Every test run, every CI execution, every fuzz input runs with contracts enabled. The logic errors that became libexpat CVEs would have been caught as contract violations before they ever reached a production build. The production binary benefits from having been developed under contract — the contracts are the process, not just the runtime check. Additionally, void safety is always active regardless of contract mode, and SPECIAL array bounds can be checked selectively on security-critical paths.

The Eiffel runtime adds size overhead.

Bundling the Eiffel runtime within the shared library will make xpact.so larger than libexpat.so. The exact figure depends on finalisation and dead code removal, but a reasonable estimate is that xpact.so will be in the 600KB-1MB range compared to libexpat.so's 200-300KB. In absolute terms this is small — well under 1MB. In an era where JavaScript frameworks routinely ship 5-10MB bundles, this is not a serious practical objection. Furthermore, with GC disabled during parsing and SCOOP not used in the minimal single-threaded build, the runtime components actually exercised are a small fraction of the full runtime.

The Eiffel developer pool is small.

This is the most significant long-term constraint. xpact needs contributors, maintainers, and reviewers. The Eiffel community is small. However, this is partly a consequence of Eiffel's visibility problem — and xpact is specifically intended to address that. A successful, visible, widely-deployed xpact library is the kind of project that attracts developers who did not previously know Eiffel existed. The Python binding in particular opens the door to a large developer community discovering Eiffel through a tool they use. The goal is not to build xpact with the current Eiffel community alone but to use xpact to grow it.

The maintainability argument is actually a partial answer to that concern: a codebase that is readable, well-contracted, and supported by mature tooling is easier for new contributors to pick up than a mechanically transpiled unsafe Rust codebase. Lower barrier to contribution partially compensates for a smaller initial pool.

Can xpact really match libexpat's performance?

This is an empirical question that requires a benchmark rather than a theoretical argument. What can be said is that the architectural choices — SPECIAL arrays for the hot path, GC disabled during parsing, string pool recycling — are specifically designed to make the performance case competitive. The experience from Eiffel-Loop with similar patterns suggests the performance story is credible. Publishing benchmark results early and honestly, including where xpact is slower and why, is the right approach.


Beyond xpact: other candidates worth considering

xpact is proposed as the highest-priority first project — the one that best satisfies all the criteria simultaneously. But the methodology applies to other targets, and the Eiffel community should be thinking about a pipeline of projects rather than a single bet.

The criteria — widely deployed, logic-error CVE history, no Rust rewrite, achievable scope, specification-driven domain, permissive licence — can be applied systematically.

libyaml is a C YAML parser with a CVE history that includes integer overflows and heap corruption. YAML is widely used in configuration files across the DevOps ecosystem. It is small — comparable to libexpat in scope. No serious Rust rewrite exists as a drop-in replacement.

A DNS stub resolver is another candidate. DNS resolution is specification-driven, security-critical, present on every networked device, and the existing C implementations have accumulated significant CVE histories. A clean Eiffel implementation with formal contracts on the packet parsing logic would be compelling.

A JSON parser is a smaller scope project — potentially appropriate as a learning exercise or proof of concept before tackling a full XML parser. JSON's grammar is simpler than XML's, the CVE surface is smaller, but the ecosystem is enormous and a well-packaged Eiffel JSON library with Python bindings would get immediate traction.

The important principle is that each project should follow the same pattern: C-compatible interface where applicable, contracts derived from the specification, CVE regression tests as the correctness baseline, and a Python binding as the community-facing distribution channel.


What success would look like for Eiffel

It is worth being concrete about what a successful xpact project would change for Eiffel's visibility.

Phase 1: credible release. xpact passes the complete libexpat test suite. The benchmark results are published — honestly, including areas where performance is not yet competitive and why. The contract annotations are visible in the public repository. This is achievable by a small team and represents the minimum credible statement.

Phase 2: Python package. The PyPI package ships. Python developers can install xpact and use it as an alternative XML parsing backend. Security-conscious Python teams have a concrete choice to make. This is where community discovery begins.

Phase 3: security community engagement. The essay arguing that xpact's DbC methodology would have prevented specific historical CVEs is published. The argument is presented at a security conference — PyCon, linux.conf.au, or a dedicated security track. The comparison with sudo-rs's logic-error CVEs makes the argument timely and concrete.

Phase 4: OSS-Fuzz integration. xpact joins libexpat in the OSS-Fuzz continuous fuzzing infrastructure. Any vulnerabilities found are fixed quickly and transparently. A clean OSS-Fuzz record over time is a durable credibility signal.

Phase 5: downstream adoption. A Linux distribution packages xpact as an alternative to libexpat. A Python project of note switches its XML processing to xpact and publishes the reasoning. These are the events that generate the kind of coverage that introduces Eiffel to developers who have never encountered it.

None of this requires Eiffel to become the next Python or Rust. It requires one well-executed, well-documented, well-argued project to demonstrate that Eiffel is a serious choice for real systems programming work. The visibility benefit accrues to the whole language from a single flagship project.


Conclusion: correctness is the next frontier

The software industry spent the last decade discovering that memory safety matters and that programming language choice can address it. The insight was correct and the progress is real. Rust has made C++ codebases meaningfully safer and the trend toward memory-safe systems languages will continue.

But the sudo-rs CVEs of 2025 illustrate a truth that the memory safety framing obscures: most bugs are not memory bugs. Most CVEs are logic errors — missing validations, incorrect state transitions, absent bounds checks, unenforced invariants. These bugs are invisible to the borrow checker. They are not solved by ownership types. They require a different tool.

That tool is Design by Contract. Bertrand Meyer understood this in 1988. Eiffel has embodied it ever since. The industry is only now approaching the point where the memory safety problem is sufficiently addressed that attention will turn to the correctness problem — the problem Eiffel was built to solve.

It is worth pausing to appreciate what Eiffel brings to this task. It would be hard to find another language that simultaneously offers:

  • Formal correctness guarantees through Design by Contract
  • Static null safety through void safety
  • Near-C performance through SPECIAL arrays
  • Native C interoperability through compile-to-C code generation, with support for x86-64, ARM64, Apple Silicon, and Raspberry Pi from the same source base
  • MSVC and GCC portability from the same source base
  • Managed concurrency through SCOOP (native build)
  • .NET deployment through CIL compilation (separate target, see foot note)
  • Decades of IDE investment in EiffelStudio
  • Selective GC control for performance-critical sections

No single one of these is unique to Eiffel. The combination in a single language, producing multiple deployment targets from one codebase, is.

xpact is a proposal for how Eiffel enters that conversation with a concrete, deployable, demonstrable answer to a real security problem affecting real systems. Not a language advocacy argument. Not a theoretical proof. A library. A drop-in replacement. A test suite with contracts. A Python package. A benchmark.

The billion users are already there, embedded in the systems that carry libexpat. The security concern is documented in a public CVE list that keeps growing. The architectural answer has existed in Eiffel for thirty years.

The essay you are reading is an argument. xpact is the proof.


Foot notes

.NET deployment: SCOOP concurrency and .NET deployment are alternative targets from the same Eiffel source base — SCOOP is not currently supported in the .NET build. The native build delivers SCOOP-based pipeline parallelism; the .NET build delivers idiomatic CLR delegate-based callbacks. Each target is optimised for its deployment context.

However, the Eiffel-Loop library provides EL_PROCEDURE_DISTRIBUTER and EL_FUNCTION_DISTRIBUTER as a practical alternative — agent-based thread pool distribution built on ISE's classic thread.ecf. (See Concurrency-demo) These abstractions are not experimental — they are production-tested in the Eiffel-View repository publisher, a tool that performs a partial parse of the entire Eiffel-Loop codebase using distributed threads every time source code is added or changed, generating the static HTML for eiffel-loop.com. The native xpact build uses SCOOP for pipeline parallelism; the .NET build can use Eiffel-Loop's battle-tested threading abstractions or the CLR's own task parallel library.

See also

References

  • Meyer, Bertrand. Object-Oriented Software Construction, 2nd ed. Prentice Hall, 1997.
  • US National Cybersecurity Strategy, Office of the National Cyber Director, 2023.
  • CISA, NSA et al. The Case for Memory Safe Roadmaps, 2023.
  • sudo-rs CVE disclosures, July 2025.
  • Ubuntu 25.10 release notes, October 2025.