Embedded System Design vs Traditional Software Design

Ian G Harris (Professor · University of California Irvine)

Blacks in Cyber Village @ DEF CON 33 · Day 1 · Blacks in Cyber Village

Overview

In this insightful tutorial, Professor Ian G Harris from the University of California Irvine demystifies the intricate world of embedded system design, drawing a clear distinction from conventional software development. Rather than presenting a novel security exploit, Harris aims to equip both aspiring attackers and defenders with a foundational understanding of how embedded systems are built. This knowledge is crucial because the unique constraints and methodologies of embedded design often lead to different types of vulnerabilities and require specialized defensive strategies.

Watch on YouTube

Visual summary for Embedded System Design vs Traditional Software Design by Ian G Harris
Visual summary for Embedded System Design vs Traditional Software Design by Ian G Harris

Key moments

  1. 0:00 Introduction: What is an embedded system?
  2. 2:15 Key difference 1: Efficiency and cost constraints
  3. 4:00 Key difference 2: Hardware-software co-design
  4. 5:15 Outline of talk: Microcontrollers, hardware, debugging
  5. 5:40 Microcontrollers: Direct hardware control vs. OS
  6. 6:40 Programming microcontrollers: Development and flashing methods

Embedded System Design vs Traditional Software Design

Speakers: Ian G Harris, Professor, University of California Irvine

Conference: Blacks in Cyber Village

YouTube: https://www.youtube.com/watch?v=S4jr6k52sNU

Overview

In this insightful tutorial, Professor Ian G Harris from the University of California Irvine demystifies the intricate world of embedded system design, drawing a clear distinction from conventional software development. Rather than presenting a novel security exploit, Harris aims to equip both aspiring attackers and defenders with a foundational understanding of how embedded systems are built. This knowledge is crucial because the unique constraints and methodologies of embedded design often lead to different types of vulnerabilities and require specialized defensive strategies.

The talk highlights that embedded systems are ubiquitous computers that don't typically look like computers—ranging from the sophisticated control units in modern cars to everyday devices like projectors and smartphones. The core message emphasizes the paramount importance of efficiency in embedded design due to stringent cost, performance, and power constraints, a stark contrast to the often resource-agnostic approach in traditional software. By dissecting the hardware/software co-design paradigm, the challenges of low-level peripheral control, and the nuances of embedded debugging, Professor Harris provides a comprehensive blueprint for understanding the security landscape of these pervasive devices.

This article summarizes and expands upon Professor Harris's key points, offering a deep dive into the technical aspects of embedded system development. It explores the unique programming models, the critical role of extensive data sheets, and the trade-offs inherent in using abstraction libraries. Furthermore, it directly addresses the defensive implications for securing these systems, drawing connections between design choices and potential attack vectors, making it an essential read for anyone involved in the security of modern hardware.

Background

▶ Watch: Introduction: What is an embedded system? (0:00)

Embedded systems are defined as computers that do not resemble traditional desktop or laptop computers. Examples include the 60+ microcontrollers in a modern car, a smartphone, or even a projector. While these devices perform complex computations, their user interfaces are typically simplified, abstracting away the underlying computational complexity. Related terms like Cyber-Physical Systems (CPS), which often focus on industrial applications with large sensors and actuators, and the Internet of Things (IoT), which emphasizes networking capabilities, are essentially specialized subsets of embedded systems. Given that most modern embedded systems now incorporate networking, the distinction between IoT and general embedded systems has largely blurred.

The fundamental difference in designing embedded systems compared to traditional software lies in the overwhelming emphasis on efficiency. Unlike typical software development where resources might be abundant (e.g., an Intel i9 processor with gigabytes of RAM), embedded system manufacturers are driven by extreme cost-consciousness. This means selecting the cheapest possible microcontroller, often sacrificing raw computational power for minimal cost. Consequently, designers must constantly consider constraints such as design cost, performance, memory usage, flash memory (storage), and crucially, power consumption. This necessitates a deep understanding of hardware specifics, a concept often overlooked in high-level software development.

Embedded systems inherently involve hardware and software co-design. A device like a conference badge, for instance, comprises a microcontroller, screen, speakers, and LEDs, all requiring software control. Unlike a Python programmer who might not consider the exact monitor or speaker, an embedded designer must account for every hardware component from the outset, influencing how the code is written. This integration means the programmer directly controls hardware features that an operating system would typically abstract away in a general-purpose computer. Microcontrollers themselves are cheaper, more directly controllable integrated circuits compared to microprocessors, allowing programmers granular control over their on-chip hardware.

Key Findings

▶ Watch: Key difference 2: Hardware-software co-design (4:00)

Professor Harris's talk reveals several key findings about embedded system design that are crucial for a security-aware audience:

  1. Efficiency is Paramount and Drives Design: Unlike traditional software, embedded systems prioritize extreme efficiency in terms of cost, power, and memory. This necessitates low-level control and resource-aware programming, directly influencing hardware choices (e.g., cheapest microcontroller) and software architecture (e.g., bare metal vs. RTOS).
  2. Direct Hardware Control via Peripherals and Registers: Embedded programming fundamentally involves direct manipulation of hardware peripherals (like GPIO, UART, ADC) through specific memory-mapped control registers. This low-level interaction is a significant departure from high-level software development where operating systems and drivers abstract hardware.
  3. The "Pain" of Data Sheets is a Source of Vulnerabilities: Comprehensive, often multi-thousand-page data sheets are the primary source of truth for microcontroller behavior. Misinterpretations, overlooked details, or even errors within these data sheets can easily lead to subtle bugs in custom drivers, which are then ripe for exploitation. This was exemplified by the MITRE Embedded CTF where teams exploited I2C driver vulnerabilities.
  4. Trade-offs in Abstraction Libraries: While vendor-specific (e.g., STM32 HAL) and vendor-independent (e.g., CMSIS, Arduino) libraries simplify development by abstracting register-level control, they introduce trade-offs. They can be less efficient (slower, larger code) and, more critically from a security perspective, can hide crucial details or introduce their own exploitable bugs, fostering a false sense of security.
  5. Specialized Debugging Tools are Essential: Standard software debuggers are insufficient for embedded systems due to the need to observe real-time electrical signals and protocol traffic. Tools like multimeters (for constant signals), oscilloscopes (for voltage over time, requiring triggering), and logic analyzers (for digital signals, often protocol-aware) are indispensable for diagnosing hardware-software interactions and identifying issues. Advanced tools like JTAG debuggers can provide runtime information including branch tracing, useful for detecting malware.

These findings collectively underscore that embedded system security requires a specialized mindset, one that appreciates the unique constraints, low-level interactions, and specific development challenges inherent in these devices.

Technical Deep Dive

▶ Watch: Outline of talk: Microcontrollers, hardware, debugging (5:15)

The technical core of embedded system design revolves around interacting directly with specialized hardware. Professor Harris outlines this process, starting with the development environment and moving through hardware control mechanisms.

Programming Microcontrollers

The journey begins on a development machine (e.g., a laptop) where code is written. Unlike traditional compilation, embedded development uses a cross-compiler, which compiles code on one architecture (e.g., x86) to run on a different architecture (e.g., ARM). Once compiled, the executable code needs to be transferred to the microcontroller's flash memory. This is achieved via a programmer device. In manufacturing, dedicated programmers are used, often placing the bare chip into a socket. For do-it-yourself development, boards like Arduino or the STMicroelectronics boards used in class integrate the programming hardware (often another smaller microcontroller with a bootloader) directly, allowing a simple USB connection to the development machine.

The dominant architecture for microcontrollers is ARM. ARM Holdings licenses its designs to various silicon manufacturers (e.g., TI, STMicroelectronics), who then produce the actual chips. This means while the underlying instruction set and core architecture might be ARM, the specific peripherals and their control mechanisms can vary significantly between vendors.

Peripherals and Direct Register Control

A microcontroller is more than just a CPU; it's a CPU connected via internal buses to a multitude of peripherals. These peripherals are specialized hardware blocks that perform specific functions, such as General Purpose Input/Output (GPIO) for controlling pins, timers for precise timing, UART for serial communication, and Analog-to-Digital Converters (ADC) for reading analog signals.

The defining characteristic of embedded programming is the direct control of these peripherals. Unlike high-level software where print() calls the operating system, which in turn calls a driver, embedded programmers directly manipulate the peripherals. This is done through registers. A register on a chip is a small storage element, typically 32 bits, that holds configuration or status information for a peripheral. To make a peripheral do something (e.g., start an ADC conversion, send data via UART), the programmer must write specific bit patterns into its associated control registers. To observe a peripheral's state (e.g., read a pin's value), the programmer reads from its status registers.

Operating System Choices

Embedded systems offer different operating system paradigms:

  • Full-on Operating System: Some embedded systems, like routers, run a stripped-down version of Linux. This offers familiar programming environments with drivers and abstractions but incurs significant overhead in terms of memory and flash usage, making it unsuitable for extremely resource-constrained devices.
  • Bare Metal: This approach involves no operating system whatsoever. The programmer has complete, direct control over all hardware and peripherals, writing code that runs directly on the microcontroller. This offers maximum efficiency and control but requires extensive knowledge of hardware details and peripheral registers.
  • Real-Time Operating Systems (RTOS): A middle ground, RTOS are lightweight operating systems designed to meet strict real-time deadlines. They typically support multi-threading and specialized scheduling algorithms to guarantee that critical tasks (e.g., sampling an audio signal 8,000 times a second) are executed within their required timeframes. They offer more abstraction than bare metal but are far more efficient than full-blown operating systems.

The Role of Data Sheets

A critical and often "painful" aspect of embedded development is the reliance on data sheets or reference manuals. These documents, like the 1,757-page example mentioned, meticulously describe every detail of a microcontroller: its peripherals, their registers, memory maps, and behavior under various conditions. Programmers must become adept at navigating these massive documents, searching for specific details, and understanding the implications of different settings.

Crucially, data sheets are a common source of bugs and vulnerabilities. A detail missed in one section (e.g., reset conditions affecting UART behavior) can lead to unexpected behavior or exploitable flaws. Even worse, data sheets can sometimes be incomplete or contain errors, making debugging exceedingly difficult.

Memory-Mapped I/O and GPIO Example

Peripheral registers are typically memory-mapped, meaning each register corresponds to a specific physical memory address. To write to a register, the program simply writes to its associated memory address. While data sheets provide these addresses, most IDEs and vendor-supplied header files offer symbolic names (e.g., GPIO_A_ODR) that resolve to these addresses, simplifying programming.

Professor Harris illustrates this with a blinking LED example using a STMicroelectronics board. To blink an LED connected to port D, pin 14:

  1. Enable Clock: To save power, peripheral clocks are often off by default. The first step is to write a specific bit to a register (e.g., RCC_AHB_whatever) to enable the clock for GPIO Port D.
  2. Set Pin Mode: GPIO pins can be configured as inputs or outputs. Another register (e.g., GPIOD_MODE_REGISTER) must be set to configure pin 14 as an output.
  3. Toggle Output: In a while loop, the program repeatedly writes a 1 or 0 to the appropriate bit (bit 14) of the Output Data Register (ODR) for Port D (e.g., GPIOD_ODR). This sets the pin high or low, turning the LED on or off, with delays in between.

This low-level interaction requires understanding not just the register, but the specific bit within the 32-bit register that controls the desired function.

Abstraction Libraries

To mitigate the tediousness of direct register manipulation, developers often use abstraction libraries:

  • Vendor-Specific: E.g., STMicroelectronics' HAL (Hardware Abstraction Layer) library.
  • Vendor-Independent: E.g., CMSIS (Cortex Microcontroller Software Interface Standard), which provides a consistent interface across ARM Cortex-M microcontrollers.
  • Platform-Specific: E.g., Arduino libraries, often ported to various microcontrollers (like ESP32).

These libraries provide higher-level functions (e.g., HAL_GPIO_TogglePin(), digitalWrite()) that abstract away the direct register writes. While they simplify development and improve portability, they come with trade-offs:

  • Efficiency: Libraries can be slower and consume more memory/flash because they often perform generalized actions or checks that might not be strictly necessary for a specific simple task.
  • Hidden Details: They abstract away the low-level hardware interactions, potentially obscuring critical operational details from the developer. This can be problematic for security, as understanding these details is vital for identifying and mitigating vulnerabilities.
  • Library Bugs: The libraries themselves can contain exploitable bugs, adding another layer of potential vulnerabilities.

Connecting to External Devices

Beyond the microcontroller itself, embedded systems frequently interact with external analog and digital devices.

Analog Interfacing

Simple analog devices include actuators (LEDs, motors) and sensors (push buttons, potentiometers). Interfacing with these often requires basic analog electronics knowledge. Ohm's Law (V=IR) is fundamental.

For an LED (Light-Emitting Diode):

  • It's a diode, allowing current flow in one direction (anode to cathode) when a sufficient forward voltage (e.g., ~1.9V) is applied.
  • To prevent damage, a current-limiting resistor is essential. For a 3.3V supply and a desired current of 10mA (a safe value for many LEDs), a 330-ohm resistor is calculated using Ohm's Law.

Physical Wiring with Breadboards

Do-it-yourselfers typically assemble circuits on a breadboard. These boards have rows of interconnected holes (e.g., five holes in a row are connected), allowing components to be easily inserted and connected without soldering. Translating a circuit schematic (drawing) into a physical breadboard layout is a core skill.

Synchronous Communication Protocols

When connecting a microcontroller to other digital hardware devices (e.g., sensors, displays), communication protocols are used. Synchronous protocols (which share a clock line) like SPI (Serial Peripheral Interface) and I2C (Inter-Integrated Circuit) are common for their speed and efficiency.

  • I2C is often used for connecting multiple "slave" devices to a "master" microcontroller using just two wires: SDA (Serial Data Line) and SCL (Serial Clock Line).
  • Like other peripherals, I2C communication is managed through dedicated registers (e.g., I2C_CR1, I2C_DR). Writing a byte to the data register (I2C_DR) sends it, and reading from it receives data, with the hardware handling the protocol specifics.

Demo / Proof of Concept

▶ Watch: Microcontrollers: Direct hardware control vs. OS (5:40)

Professor Harris used the classic blinking LED as a primary demonstration of low-level embedded programming. The demonstration involved an STMicroelectronics development board, specifically identifying an on-board LED connected to Port D, Pin 14.

The core of the demo involved presenting two approaches to making the LED blink:

  1. Direct Register Manipulation (Bare Metal):
  • Initialization: The code first performs crucial setup steps by directly writing to memory-mapped registers. This includes enabling the clock for GPIO Port D (by setting a specific bit in a clock control register like RCC_AHB_...) and configuring Pin 14 as an output pin (by setting specific bits in the GPIOD_MODE_REGISTER).
  • Blinking Logic: Inside a while(1) loop, the program directly writes a 1 to bit 14 of the Output Data Register (ODR) for Port D to turn the LED on, followed by a software-based delay loop. Then, it writes a 0 to the same bit to turn the LED off, followed by another delay. This demonstrates the granular control required at the bare-metal level. The speaker visually presented the C code snippet for this, showing explicit address assignments and bit-shifted operations (e.g., (volatile uint32_t)0x40020C00 |= (1 << 3);).
  1. Using Abstraction Libraries:
  • STM32 HAL Library: An example was shown using the STMicroelectronics HAL library. This involved calling functions like HAL_Init(), HAL_GPIO_Init(), HAL_GPIO_TogglePin(), and HAL_Delay(). This illustrates how libraries abstract away the direct register access, making the code more readable and less tedious but potentially less efficient.
  • Arduino Library: For comparison, the equivalent Arduino code was presented, utilizing its setup() and loop() functions and digitalWrite() for pin control. This further highlighted the different levels of abstraction available.

Beyond the on-board LED, Professor Harris extended the concept to external analog devices. He discussed how to wire an external LED to a microcontroller pin via a breadboard. This involved using a current-limiting resistor (e.g., 330 ohms for a 3.3V supply and 10mA current) in series with the LED to prevent it from burning out. The physical setup involves connecting the microcontroller's output pin, the resistor, and the LED (anode to resistor, cathode to ground) on the breadboard, emphasizing the need to translate schematic diagrams into physical wiring.

The talk also touched upon connecting external digital hardware using synchronous communication protocols like I2C. While not a live coding demo, the concept of wiring multiple I2C-compatible slave devices (e.g., a sensor and an LCD display) to an Arduino's SDA and SCL pins was explained, with the microcontroller acting as the master, controlling communication through its dedicated I2C peripheral registers.

Defensive Implications

▶ Watch: Programming microcontrollers: Development and flashing methods (6:40)

Understanding embedded system design is not merely an academic exercise; it has profound defensive implications for securing these increasingly ubiquitous devices. Professor Harris explicitly links design choices and development practices to potential vulnerabilities.

  1. Vulnerabilities from Data Sheet Misinterpretation: The sheer complexity and length (e.g., 1,757 pages) of microcontroller data sheets are a significant source of bugs. A developer might misinterpret a peripheral's behavior, overlook a critical detail (like specific reset conditions affecting a communication protocol), or even encounter errors in the data sheet itself. These misunderstandings lead to flaws in custom drivers or firmware logic. From a defensive standpoint, this means that even well-intentioned, custom-written code can harbor subtle vulnerabilities that are difficult to spot without an intimate understanding of the hardware. Defenders should, therefore, prioritize thorough code reviews against data sheet specifications and consider fuzzing custom drivers, as discussed below.
  1. Driver Vulnerabilities and Fuzzing: Professor Harris directly references the MITRE Embedded Capture The Flag (CTF), where a team from CMU successfully exploited vulnerabilities in I2C drivers. His conclusion, based on the complexity of such a task, is that they likely achieved this through fuzzing—feeding malformed or unexpected inputs to the driver until an exploitable bug was discovered. This highlights a critical defensive strategy: any custom driver, especially for communication protocols like I2C or SPI, should be subjected to rigorous fuzzing to uncover edge-case vulnerabilities that might arise from incomplete error handling or unexpected hardware states. Firmware teams should integrate fuzzing into their testing pipeline for all custom-developed peripheral drivers.
  1. Security Risks of Abstraction Libraries: While libraries (e.g., STM32 HAL, CMSIS, Arduino) simplify development, they introduce their own security concerns. They abstract away low-level details, potentially preventing developers from understanding the full implications of their code's interaction with hardware. More directly, the libraries themselves can contain exploitable bugs. Relying on these libraries without understanding their underlying implementation or thoroughly vetting them can introduce vulnerabilities beyond the developer's direct control. Defenders should ensure that libraries used are well-maintained, regularly updated, and ideally, subjected to security audits. If resource constraints allow, understanding the underlying register operations is always safer.
  1. Promoting Security Awareness in Firmware Teams: Professor Harris notes the challenge of engaging traditional firmware teams with cybersecurity. His recommendations for establishing rapport include:
  • "Scare Tactics": Presenting real-world documented attacks on industrial control systems or similar embedded devices can effectively demonstrate the tangible risks and importance of security.
  • Participating in CTFs: Engaging in hands-on challenges like the MITRE Embedded CTF can be "eye-opening." Teams build a secure system, then attack others, providing direct experience with both defense and offense. This practical exposure can profoundly shift perspectives.
  1. Advanced Debugging for Malware Detection: While not covered in depth, the discussion on debugging tools touched upon advanced capabilities like JTAG debuggers and Embedded Trace Macro Cells (ETMs). These tools, though expensive, can provide real-time runtime information, including tracing CPU branches. This capability is invaluable for detecting malware; if a buffer overflow changes a return address, an ETM can detect the anomalous branch to an unexpected memory location, providing a powerful forensic and real-time detection mechanism for sophisticated attacks.

In summary, defending embedded systems requires a deep appreciation for their unique design constraints and technical intricacies. It demands meticulous attention to detail in data sheet interpretation, aggressive fuzzing of custom drivers, careful selection and vetting of abstraction libraries, and a proactive approach to fostering security awareness within development teams.

Key Takeaways

  • Efficiency is the Prime Driver: Embedded system design prioritizes extreme efficiency (cost, power, memory) over ease of development, leading to vastly different design choices compared to traditional software.
  • Direct Hardware Control is Fundamental: Embedded programming involves direct manipulation of hardware peripherals through memory-mapped control registers, requiring a deep understanding of the microcontroller's architecture and its data sheet.
  • Data Sheets are Critical but Challenging: Comprehensive data sheets (often thousands of pages long, e.g., 1,757 pages) are the primary source of truth but are prone to misinterpretation, overlooked details, or even errors, which can lead to exploitable bugs in firmware.
  • Abstraction Libraries Offer Trade-offs: While vendor-specific (e.g., STM32 HAL) and vendor-independent (e.g., CMSIS, Arduino) libraries simplify development, they can introduce inefficiencies, hide crucial low-level details, and may contain their own exploitable vulnerabilities.
  • Specialized Debugging Tools are Essential: Tools like multimeters, oscilloscopes (with triggering), and logic analyzers are indispensable for diagnosing hardware-software interactions, observing real-time signals, and identifying issues that standard software debuggers cannot.
  • Security Requires Low-Level Understanding and Proactive Testing: Vulnerabilities often stem from driver bugs (e.g., I2C fuzzing in MITRE CTF), data sheet misinterpretations, or flaws in libraries. Defensive strategies must include fuzzing custom drivers, careful library selection, and fostering security awareness within firmware development teams through education and practical exercises like CTFs.

About the Speaker(s)

Professor Ian G Harris is a faculty member at the University of California Irvine. He regularly teaches courses related to embedded system design, providing students with the foundational knowledge and practical skills necessary to understand and develop these specialized computing systems. His expertise in the field is evident in his ability to distill complex topics into an accessible tutorial format, emphasizing the practical challenges and unique considerations of embedded development.

Reviews

Dr. Zero (Offensive Security Researcher) — SOLID

A competent introductory tutorial on embedded system fundamentals — register-level GPIO control, data sheet navigation, abstraction trade-offs, debugging toolchains — aimed at a security-adjacent audience that likely hasn't touched a microcontroller. Solidly executed for what it is, but this is survey content, not research, and it belongs in a university course catalog more than a security conference program.

Heather Calloway (CISO) — WEAK

Solid embedded systems tutorial with competent coverage of firmware fundamentals and some honest acknowledgment of security implications — but it stays firmly in the classroom. The defensive claims are surface-level and the institutional, governance, and operator dimensions are essentially absent.

→ Top-rated talks at Blacks in Cyber Village @ DEF CON 33

All talks from Blacks in Cyber Village @ DEF CON 33