Memory Safety & Security: A Note
Why SuperJ doesn't have a borrow checker, and why that's the right tradeoff for most software.
The short version
Rust's borrow checker makes one class of bug impossible — memory safety bugs (use-after-free, double-free, buffer overflow, data race) — and charges a permanent per-line tax to get it: every lifetime annotation, every borrow-graph fight, every Rc<RefCell<>> you reach for when the checker can't prove what's obviously fine, paid on the first write and on every subsequent edit, by every engineer, for the life of the project. That tax compounds — it slows onboarding, slows iteration, turns small refactors into borrow archaeology, and pushes people to avoid changes they'd otherwise make.
SuperJ charges none of that tax. You write Java. In exchange, memory bugs are possible — they crash at runtime instead of being rejected at compile time. The question is whether one bug class being impossible is worth a permanent tax on everything you ever write.
The answer, for most software: no
The bug class the borrow checker eliminates — memory corruption as a route to code execution — is a serious one, but it is a narrow one. It is the class that drove two decades of browser and kernel security patches, and for that exact software it is worth the tax:
- Browsers — attacker-controlled input runs in your process; billions of users; a memory bug = remote code execution = you are distributing malware to a billion people.
- Kernels — a memory bug = root for everyone running it, and you cannot hotpatch a kernel without a reboot.
- Sandbox boundaries / parsers for untrusted binary formats — same logic, smaller scale.
This is the list of software where one memory bug is catastrophic and the bug surface is unbounded. It is a real list. It is also a small fraction of what gets written.
For everything else — servers, services, API backends, data pipelines, network daemons, the actual bulk of production software — the threat model is not "attacker gets code execution via memory corruption." It is "the logic returns the wrong answer" or "the service crashes under load." Memory safety is irrelevant to both. The CVE databases skew toward memory bugs only because memory bugs are the ones that get assigned CVE numbers; logic bugs cause incidents that never get filed as vulnerabilities. If you weight by bugs that actually hurt users in production, logic bugs dominate, and the borrow checker does nothing about them.
What SuperJ gives you instead: determinism
This is the other half of the trade, and it is the half the standard framing leaves out. SuperJ's single-threaded + SEDA model gives you determinism: the same event sequence produces the same state, bit-for-bit. That buys you a property no memory-safe concurrent system provides:
- Every bug is reproducible. Replay the event journal, get the same crash, fix it, and it stays fixed — because the same inputs give the same behavior. The system converges toward correctness.
- The behavior space is one-dimensional (parameterized by input), not n-dimensional (parameterized by input × every possible interleaving). That makes formal verification, model checking, and exhaustive fuzzing tractable — you are not quantifying over a scheduler you don't control.
- No timing-dependent behavior, therefore no timing attack surface. In a concurrent system, an attacker who can influence scheduling — by loading cores, sending signals, racing on a lock — can influence which output you produce. That is a real exploit class (TOCTOU, scheduler-based races, cross-thread cache-timing side channels). Rust's memory safety says nothing about it; the concurrency model it enables creates it.
The sharp version: **memory safety is a negative property ("this bug class cannot happen") obtained by introducing nondeterminism, which is a positive source of insecurity ("behavior now depends on things an attacker can influence").** The borrow checker removes one failure mode and the concurrency model opens a different, larger one. Determinism closes the second by construction, and makes the first reproducible and fixable.
The economic comparison
| Rust | SuperJ | |
|---|---|---|
| Per-line cost | High, permanent, paid on every edit | Low — Java ergonomics |
| What it guarantees | One bug class impossible (memory) | One property present (determinism) |
| What it doesn't help | Logic bugs, deadlocks, the other ~90% | Memory bugs (until you fix them, which you can) |
| Convergence model | "It compiles, therefore no memory bugs" (silent on everything else) | "It reproduces, therefore you can fix any bug" |
The asymmetry is the point. Rust spends enormous effort eliminating a minority failure mode and introduces nondeterminism that makes the majority failure mode harder to fix. SuperJ spends no effort on the minority mode and gives you a workflow that makes the majority mode tractable.
When to reach for which
- Rust — when a single memory bug is catastrophic and the bug surface is unbounded: browsers, kernels, sandboxes, parsers for untrusted binary formats. The per-line tax buys insurance against a threat model you actually have.
- SuperJ — for the ~95% of software that isn't that: servers, services, data systems, network daemons. The borrow checker's tax buys insurance against a threat model you mostly don't have, while determinism buys you a workflow that fixes the bugs you actually ship.
The reason the industry treats memory safety as universally worth it is that "memory safety" sounds like an obvious good and "ergonomic cost" sounds like a complaint — when in fact the first is a narrow guarantee and the second is a pervasive tax. You pay a lot for a little, in most cases. SuperJ's thesis is that the one class of bug Rust makes impossible is not worth the cost of making it impossible, for most software — and determinism, which costs almost nothing, gives you a workflow that fixes everything else.
The ideal (and why nobody ships it)
Single-threaded Rust has both properties: the borrow checker's memory safety with no concurrency-induced nondeterminism. That is strictly better than either pure option. It is also the configuration nobody talks about, because it defeats the marketing — "fearless concurrency" loses its point if you don't use the concurrency. SuperJ's bet is that for most software you don't need the concurrency, and giving up the memory safety to get the ergonomics is the better trade than giving up the ergonomics to keep a guarantee that only matters for a threat model you don't have.