A memory error is a program mishandling its own storage. The program reads or writes outside an allocation, uses memory after freeing it, leaks it, or runs out of it. The class is narrow and well understood, yet it remains one of the most exploited bug classes in languages without memory safety (Szekeres et al. 2013)1.
Memory errors fall into two kinds. Safety violations break the language's own rules and are undefined behavior, so the program may then do anything (Szekeres et al. 2013)1. Management errors leave behavior defined but use storage wrongly: memory or a resource is leaked, exhausted, or fragmented. The two kinds need different methods, and no single method covers both.
Safety violations¶
Spatial safety covers buffer overflow and underflow and out-of-bounds reads and writes (Nagarakatte et al. 2009)2. Absence is proved or inferred by abstract interpretation, bounded model checking, symbolic execution, and theorem proving; flagged heuristically by linters on C/C++; and caught at runtime by fuzzing under ASan. A memory-safe language rules it out too, but from runtime bounds checks and a managed heap, not the type checker: an ordinary static type does not bound an array index, though a refinement or dependent type can prove index bounds directly.
Temporal safety covers use-after-free, double-free, use-after-return, and
invalid free (Nagarakatte et al. 2010)3. Affine ownership closes it by construction: linear
types reject a value used after it is moved or freed in
the safe subset. Otherwise a garbage collector retires it, or
fuzzing under ASan finds it at runtime, with
Miri for unsafe Rust.
Null dereference and type confusion are the whole of what ordinary static typing
closes on its own. A null dereference is ruled out by non-nullable and option types:
static types that distinguish T from T? (Kotlin,
Swift, Rust's Option, Haskell's Maybe, TypeScript under strictNullChecks) make
a null value unrepresentable where one is not expected. Type confusion
(reinterpreting bytes as the wrong type) is forbidden by a sound type system,
which cannot express the reinterpretation; it survives only where an unsafe cast or
union escapes the checker (C/C++), where static
analysis and UBSan
catch what is left. Spatial and temporal safety, by contrast, need the runtime, an
analysis, or an affine type.
Uninitialized reads are proved absent statically by abstract interpretation and caught at runtime by MSan and Valgrind memcheck.
Data races, two threads accessing one location without synchronization and at
least one of them writing, are the concurrency case of memory safety. Rust's Send and Sync bounds close them by
construction (linear types); otherwise systematic
concurrency testing and
TSan find them.
Management errors¶
Leaks come in two shapes. The unreachable leak, memory whose last pointer is lost, is impossible for a resource held under an affine linear type, and found otherwise by heap profilers (heaptrack, dhat, scalene, Valgrind massif; see profiling) and LSan. The logical leak, memory still reachable but never used again, is the one a garbage collector cannot reclaim and reachability tools miss; it shows up only as growth, caught by heap profiling and by soak testing under load.
Resource lifecycle: a file, socket, or lock must be released exactly once, the same discipline as temporal safety. It is closed by construction by linear types or by RAII, and its error paths are exercised with fault injection.
Out of memory is a reliability concern: the question is whether the
allocation-failure path is both present and correct. Its presence can be forced by
construction where allocation is fallible and typed as an error the caller must
discharge (Zig's error{OutOfMemory} unions, Rust's try_reserve returning a
Result), and flagged otherwise by static
analysis reporting an unchecked
allocation. Its correctness is exercised at runtime: fault
injection with failing allocators is the direct
method, deterministic simulation
testing drives the allocator to fail
on a fixed schedule, and soak and spike load
testing reach exhaustion under sustained or bursty
demand.
Fragmentation, where allocation fails though enough total memory is free, surfaces only over long runs, caught by soak load testing and allocator instrumentation via profiling.
Garbage collection is a trade, not a win¶
Garbage collection is the mainstream way to get memory safety without an ownership
discipline. It eliminates the temporal-safety class and the unreachable leak: a live
pointer keeps memory alive, so use-after-free and double-free cannot occur in safe
code. The logical leak survives, since a reachable
object is never collected; non-memory resources still need explicit release, which
is why try-with-resources, defer, and RAII exist alongside it; and pause times
and a memory-footprint overhead disqualify it for hard-real-time and tight-memory
targets. Use-after-free can also reappear through unsafe code, native interop, or
off-heap buffers. Collection is not exclusive with substructural typing:
Pony pairs its reference capabilities with ORCA, a concurrent
collector that relies on the data-race freedom those capabilities guarantee to let
each actor collect its own heap without stop-the-world pauses (Clebsch et al. 2017)4.
Referenced by¶
- Linear types · Methods
- Linters · Methods
- Schema and boundary validation · Methods
- Static analysis · Methods
References¶
-
Szekeres, Laszlo, Mathias Payer, Tao Wei, and Dawn Song. 2013. "SoK: Eternal War in Memory." 2013 IEEE Symposium on Security and Privacy, 48–62. https://doi.org/10.1109/SP.2013.13. ↩↩
-
Nagarakatte, Santosh, Jianzhou Zhao, Milo M. K. Martin, and Steve Zdancewic. 2009. "SoftBound: Highly Compatible and Complete Spatial Memory Safety for C." Proceedings of the 30th ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI '09), 245–58. https://doi.org/10.1145/1542476.1542504. ↩
-
Nagarakatte, Santosh, Jianzhou Zhao, Milo M. K. Martin, and Steve Zdancewic. 2010. "CETS: Compiler Enforced Temporal Safety for C." Proceedings of the 2010 International Symposium on Memory Management (ISMM '10), 31–40. https://doi.org/10.1145/1806651.1806657. ↩
-
Clebsch, Sylvan, Juliana Franco, Sophia Drossopoulou, Albert Mingkun Yang, Tobias Wrigstad, and Jan Vitek. 2017. "Orca: GC and Type System Co-design for Actor Languages." Proceedings of the ACM on Programming Languages 1 (OOPSLA): 72:1–28. https://doi.org/10.1145/3133896. ↩