Elevators, Memory Bugs, and the Birth of Rust

Dec 2, 2024

Elevators are deceptively complex embedded systems. Beyond their mechanical components, the real magic — and the real peril — lies in the software controlling their behavior. Embedded controllers handle sensor inputs, motor commands, safety interlocks, and real-time scheduling, all under tight resource constraints and strict safety requirements.

What’s fascinating, and rarely discussed, is how the software reliability challenges in elevator control systems inspired one of the most influential modern programming languages: Rust. The story is more than an anecdote; it’s a deep insight into why memory safety matters — and how Rust’s design tackles bugs that have haunted low-level systems programming for decades.

Elevators as Real-Time Embedded Systems

An elevator’s control system is a classic example of a real-time embedded system. The controller interacts with hardware inputs like door sensors, floor detectors, motor encoders, emergency stops, and weight sensors. The software must respond deterministically and with minimal latency to guarantee safety and user experience.

The software runs on microcontrollers or embedded processors with constrained CPU power and memory. It usually operates bare-metal or atop a lightweight real-time operating system (RTOS). The programming language of choice traditionally has been C or C++, prized for their low-level hardware access and minimal overhead.

But here lies the problem: these languages offer great power — direct memory manipulation, pointer arithmetic, manual resource management — at the cost of safety. Subtle bugs like buffer overflows, dangling pointers, use-after-free errors, and race conditions are common pitfalls. In the context of elevators, such bugs can cause the software to misinterpret sensor data, fail to stop the cabin, or corrupt critical state, leading to unexpected halts or, worse, unsafe behavior.

Memory Bugs

Consider a scenario where the elevator’s door sensor data is stored in a buffer managed by the software. A buffer overflow or off-by-one error here might overwrite adjacent memory that stores the motor state. This subtle memory corruption can cause the motor to run unexpectedly or stall abruptly.

Another example is dangling pointers: if the software frees a resource but continues to access it due to outdated pointers, the control logic might operate on invalid data. In elevators, this can translate to faulty decisions about stopping, door opening, or emergency braking.

Race conditions also pose serious risks, especially in multitasking RTOS environments. If the controller’s concurrency primitives are weak or misused, shared sensor states can be read inconsistently, leading to unpredictable control commands.

Such bugs are often nondeterministic and hard to reproduce, making testing and debugging embedded elevator software an enormous challenge.

Graydon Hoare’s Elevator and the Spark Behind Rust

Graydon Hoare, Rust’s creator, once shared how his apartment elevator frequently broke down because of software glitches related to memory bugs in its embedded system. These failures were frustrating but also illuminating — they underscored the inherent risks of using unsafe programming languages for safety-critical embedded software.

Hoare’s insight was that the class of bugs plaguing elevators is fundamentally tied to how traditional systems programming languages handle memory management: manual, error-prone, and unchecked at compile-time.

He envisioned a new programming language that guarantees memory safety without sacrificing low-level control and performance. This vision gave birth to Rust.

Rust’s Ownership Model

Rust’s innovation lies in its ownership and borrowing system, enforced statically by the compiler.

  • Ownership means every piece of data has a single owner responsible for cleaning it up. This eliminates double frees and use-after-free bugs.

  • Borrowing allows multiple parts of the code to temporarily access data without taking ownership, controlled via mutable and immutable references.

  • Lifetimes track how long references are valid, preventing dangling pointer dereferences.

Together, these rules prevent entire categories of memory errors before the program runs. The Rust compiler enforces these at compile-time, producing zero-cost abstractions that are as efficient as C/C++.

For embedded systems like elevators, this means software can safely manage hardware buffers, sensor inputs, and actuator commands without fear of memory corruption or race conditions.

Concurrency Without Fear

Elevator controllers often run concurrent tasks: monitoring sensors, processing user inputs, controlling motors, and handling emergency interrupts. Concurrency bugs like data races are common in traditional languages.

Rust’s type system enforces thread safety by requiring that shared mutable state be explicitly synchronized or avoided. The Send and Sync traits mark types safe to transfer or share between threads, and the borrow checker prevents data races by disallowing simultaneous mutable accesses.

This concurrency model allows embedded developers to write parallel code confidently, reducing one of the most insidious sources of bugs in real-time embedded systems.

Rust in Embedded and Safety-Critical Systems

Since its inception, Rust has seen growing adoption in embedded programming, from microcontrollers to IoT devices and safety-critical systems like automotive and aerospace.

Its ability to guarantee memory safety and prevent concurrency issues without a garbage collector makes it uniquely suited for constrained environments that demand predictability and reliability.

For elevator systems, this means the possibility of embedded control software that is provably free of memory safety bugs, reducing downtime, maintenance costs, and increasing passenger safety.

Why Not Safer C++ or Static Analyzers?

You might ask: C++ has added safety features over the years, and static analysis tools can catch bugs — so why reinvent the wheel with Rust?
The answer is that while C++ can be used safely, its safety depends on discipline, coding standards, and tools that are imperfect and often post-hoc.

Rust enforces safety by design, at the language and compiler level, eliminating whole classes of bugs before the program runs. This shifts the burden from testing and code review to the compiler, a more scalable and reliable approach. Static analyzers are helpful but cannot guarantee absence of bugs, especially in complex concurrent embedded systems. Rust’s compile-time guarantees are stronger and more holistic.

Elevator Failures and Software Reliability

Elevators epitomize why software reliability matters deeply in physical systems. Failures cause not just crashes but can impact human safety and comfort. Memory bugs lurking in low-level embedded code are a silent but pervasive threat.

The story of Rust’s genesis rooted in elevator software failures reminds us that better programming languages aren’t just about developer productivity — they are critical infrastructure for building safe, reliable systems.

Elevator control software is a microcosm for embedded systems at large — constrained environments where safety is paramount, and bugs are unforgiving.

The Future of Safe Systems Programming

The elevator story is far from over. As systems become more interconnected and complex — from smart buildings to autonomous vehicles — the need for safe systems programming languages grows.

Rust’s principles are influencing new language designs, verification tools, and software engineering practices. Its model shows us a path where low-level control and absolute safety aren’t trade-offs but complementary goals.

For embedded engineers and systems programmers, embracing Rust and its safety-first philosophy can lead to more robust, maintainable, and secure software — whether it’s an elevator, a drone, or the next generation of operating systems.

Conclusion

In the end, elevators teach us a powerful lesson: the software that controls physical machinery must be treated with the utmost rigor. Memory bugs are not just programming nuisances but fundamental threats to safety and reliability.

Rust was born from this insight — from the frustration of elevator software failures and the determination to build a language that eliminates such bugs at the root.

The next time you step into an elevator, think about the unseen code running inside, and how advances in systems programming languages like Rust are quietly making those rides safer and more dependable.


Footnotes

  1. Real-time embedded system: A system where software must respond to inputs or events within strict timing constraints. In elevators, delays in response could cause safety hazards.

  2. Buffer overflow: Occurs when a program writes data beyond the boundaries of allocated memory, corrupting adjacent data, leading to undefined behavior.

  3. Dangling pointer: A pointer that references memory that has already been freed. Using it can cause unpredictable results or crashes.

  4. Race condition: A flaw where the system’s behavior depends on the sequence or timing of uncontrollable events, often due to unsynchronized access to shared data in concurrent code.

  5. Ownership (Rust): A compile-time system ensuring that every data value has a single owner responsible for its lifecycle, preventing many memory errors.

  6. Borrowing (Rust): Mechanism allowing code to use references to data without taking ownership, tracked and enforced by the compiler to avoid invalid accesses.

  7. Lifetimes (Rust): Annotations that help the compiler verify how long references remain valid, preventing dangling pointers.

  8. Send and Sync traits: Rust’s type system markers that indicate whether types can be safely transferred or accessed concurrently across threads.

  9. Zero-cost abstractions: Programming abstractions that compile down to highly efficient machine code, adding no runtime overhead compared to manual implementations.

  10. Static analysis tools: Programs that analyze code without executing it to find potential bugs, but generally cannot guarantee total absence of errors.

https://bitzany.netlify.app/posts/feed.xml