A program execution is memory safe iff none of the following occur:
- Access errors
Reading or writing memory the program does not have permission to touch. - Buffer overflow or over-read
Writing or reading past the end of an allocated buffer. - Invalid pointer
A pointer that does not refer to a valid, live object. - Race condition
Concurrent accesses to the same memory produce a result that depends on timing. - Use after free
Accessing memory through a pointer after it has been freed. - Uninitialized variable or pointer access
Reading a variable or pointer before it has been assigned a value. - Null pointer access
Dereferencing a pointer that points to no object. - Memory leak
Allocated memory that is never freed, even though nothing still references it. - Stack or heap overflow
Writing past the bounds of a stack frame or heap allocation, corrupting adjacent memory. - Invalid free
Freeing memory that was not allocated, or freeing it more than once. - Unwanted aliasing
2 pointers unexpectedly referring to the same memory, so a write through 1 affects the other.
Spatial Violation
A memory access outside an object’s bounds.
- Buffer overflow
- Out-of-bounds read, e.g. Heartbleed
- Null pointer dereference
Temporal Violation
A memory access referring to an invalid object.
Examples:
- Buffer overflows
- Use after free
- Double free
- Use of uninitialized memory
Memory Safe Languages
A memory safe language enforces memory safety at compile time or run time.
Examples: Rust, Java, Python.
C and C++ are not memory safe and still widely used. Because:
- supported nearly everywhere
- low-level code such as operating systems cannot easily be implemented in memory safe languages.
Adopting a memory safe language costs runtime overhead and loses some direct, low-level memory access such as hardware-specific instructions needed by device drivers. C and related languages allow direct memory access without bounds checks.