Buffer Overflow Attack Taxonomy

Buffer Overflow Attack Taxonomy in C/C++: A Practical Perspective for Embedded Systems

Ranjith Tharayil Published: June 2025

Keywords: Buffer Overflow, Embedded Systems, Memory Corruption, C/C++ Security, Secure Software Engineering, Exploit Mitigation, Low-Level Systems Security

Abstract

Buffer overflows are one of the oldest and most impactful classes of software vulnerabilities, particularly prevalent in embedded systems programmed in C and C++. The combination of direct memory access, manual memory management, and the absence of runtime safety checks makes such environments highly susceptible to memory corruption attacks. This paper presents a categorized taxonomy of buffer overflow vulnerabilities relevant to embedded systems, supported by code-level illustrations, exploitation insights, and secure programming guidelines. The goal is to equip embedded developers, security engineers, and system architects with a structured understanding of these vulnerabilities and their real-world implications.

1. Introduction

Embedded systems continue to form the backbone of modern computing infrastructure — from consumer electronics and automotive ECUs to critical industrial controllers. In these domains, efficiency, determinism, and low-level hardware control take precedence, making C and C++ the dominant languages. However, their lack of built-in memory safety and reliance on direct pointer manipulation have long exposed embedded applications to a wide spectrum of vulnerabilities.

Among them, buffer overflows remain both persistent and dangerous, capable of undermining system integrity, leaking sensitive data, or enabling full system compromise. This paper aims to systematically classify the major types of buffer overflow vulnerabilities, highlighting how each manifests in embedded software, and what practical steps can be taken to mitigate them.

2. Classification of Buffer Overflow Vulnerabilities

Each category in the taxonomy is explained with a concise description, typical exploitation vector, consequences, and secure coding recommendations.

2.1 Stack-Based Buffer Overflow

Overview: A classic vulnerability where data written to a local stack buffer overflows into adjacent memory — commonly overwriting the saved return address.

Attack Vector: Stack smashing; redirecting execution flow via corrupted control data.

Impact: Arbitrary code execution, system takeover.

Example:

void vulnerable() {
    char buf[64];
    gets(buf);  // No bounds checking — highly unsafe
}

Mitigation:

  • Replace gets and similar unsafe functions with fgets or safer alternatives.
  • Compile with stack protection flags (-fstack-protector, -fstack-protector-strong).
  • Enable ASLR and DEP at the OS level where applicable.

2.2 Heap Overflow

Overview: Data exceeding the bounds of heap-allocated memory can corrupt adjacent objects or heap metadata, leading to serious exploitation opportunities.

Attack Vector: Overwrite of allocator control structures or neighboring heap blocks.

Impact: Use-after-free conditions, arbitrary memory writes, or control flow hijacking.

Example:

char *buf1 = malloc(64);
char *buf2 = malloc(64);
strcpy(buf1, attacker_input);  // May overflow into buf2

Mitigation:

  • Use strncpy and validate input lengths.
  • Consider memory allocators with built-in checks (e.g., hardened malloc).
  • Isolate critical data structures from buffers accessible by untrusted inputs.

2.3 Off-by-One Overflow

Overview: A subtle variant where a loop or copy operation writes one byte beyond the intended boundary, possibly corrupting adjacent metadata like canaries or pointers.

Attack Vector: Overflow by a single byte to affect control-sensitive fields.

Impact: Security bypass, integrity loss of stack frames or allocator metadata.

Example:

char buf[8];
for (int i = 0; i <= 8; i++)  // Off-by-one bug
    buf[i] = input[i];

Mitigation:

  • Ensure loop conditions are correct (i < N vs i <= N).
  • Use sizeof() and static analysis tools to detect off-by-one patterns.

2.4 Arbitrary Memory Write (AMW)

Overview: Occurs when user-controlled input is used to determine a write target, often via unchecked pointer dereferencing.

Attack Vector: Gaining control over a write destination pointer.

Impact: Overwriting of function pointers, configuration, or control flags.

Example:

void write_anywhere(char* addr, char val) {
    *addr = val;  // Dangerous if addr is attacker-controlled
}

Mitigation:

  • Validate all externally-supplied pointers.
  • Apply taint analysis where feasible.
  • Isolate privileged memory regions.

2.5 ARC Injection (Altered Return Code)

Overview: Overwriting return addresses or function pointers to divert execution flow to attacker-supplied locations.

Attack Vector: Exploiting function pointer writes or stack corruption.

Impact: Execution of attacker-defined code.

Example:

void (*func_ptr)() = safe_function;
strcpy((char *)&func_ptr, input);  // Overwrites function pointer
func_ptr();  // May call arbitrary code

Mitigation:

  • Harden usage of function pointers.
  • Enforce Control Flow Integrity (CFI) where supported.

2.6 Stack Canary Overwrite

Overview: Stack canaries are placed before return addresses to detect corruption. Attackers may try to overwrite them predictably or undetectably.

Attack Vector: Overflows that include the canary region.

Impact: Bypassing stack protection mechanisms.

Example:

char buf[64];
read(STDIN_FILENO, buf, 128);  // Overflows into canary

Mitigation:

  • Use -fstack-protector-strong or similar options.
  • Ensure that canaries are randomized per execution.

2.7 Use-After-Free (UAF)

Overview: Accessing memory after it has been deallocated results in undefined behavior. Attackers may reuse freed memory to inject malicious content.

Attack Vector: Dangling pointer usage.

Impact: Data corruption, remote code execution.

Example:

char *p = malloc(64);
free(p);
strcpy(p, input);  // p is invalid

Mitigation:

  • Set pointers to NULL after free.
  • Use C++ smart pointers (std::unique_ptr, std::shared_ptr) where applicable.
  • Integrate memory sanitization tools (e.g., ASan).

2.8 Format String Vulnerability

Overview: Occurs when unvalidated user input is passed as the format string, enabling unintended memory access.

Attack Vector: printf(user_input) without format specifiers.

Impact: Memory disclosure, control flow hijack via %n.

Example:

printf(user_input);  // Never do this

Mitigation:

  • Always specify the format explicitly (printf("%s", user_input)).
  • Conduct format string fuzzing for input points.

2.9 Integer Overflow → Buffer Overflow

Overview: A calculation determining buffer size overflows, leading to allocation of insufficient memory and subsequent buffer overrun.

Attack Vector: Arithmetic overflows during size computation.

Impact: Memory corruption due to under-allocated buffer.

Example:

int len = user_input_len * sizeof(int);  // May overflow
int* buf = malloc(len);
memcpy(buf, src, user_input_len);  // Actual copy exceeds buffer

Mitigation:

  • Use checked arithmetic (__builtin_mul_overflow in GCC/Clang).
  • Validate all size and length parameters before allocation.

3. Summary Table

Category Attack Vector Common Trigger Result
Stack Overflow Stack smashing Local buffer overflow Return address overwrite
Heap Overflow Metadata overwrite Overflow on heap alloc Arbitrary memory write
Off-by-One Subtle overwrite Loop or copy boundary Canary/pointer corruption
Arbitrary Write Pointer manipulation Unchecked pointer use Control/data overwrite
ARC Injection Return address corruption Stack or pointer write Code redirection
Canary Overwrite Canary bypass Oversized input Defeated stack protection
Use-After-Free Dangling pointer Access after free Use of attacker-controlled data
Format String Format injection Unchecked format usage Memory disclosure or write
Integer Overflow Allocation miscalculation Size arithmetic error Buffer overflow after malloc

4. Recommendations for Secure Embedded Development

  1. Avoid unsafe memory functions like gets, strcpy, sprintf. Prefer safer alternatives such as fgets, snprintf, strncpy.
  2. Enable compiler-level mitigations: Use flags such as -fstack-protector-strong, and ensure OS-level protections like DEP/NX and ASLR are active.
  3. Adopt static/dynamic analysis tools: Tools like Cppcheck, Clang Static Analyzer, Valgrind, and AddressSanitizer (ASan) can catch many classes of memory bugs early.
  4. Design for memory safety: Where possible, apply secure C subsets (e.g., MISRA C) or migrate critical components to memory-safe languages like Rust.
  5. Review third-party libraries carefully, especially those handling input parsing, buffer operations, or external interfaces.

5. References

  1. OWASP C++ Secure Coding Practices: https://owasp.org/www-project-secure-coding-practices/
  2. MITRE CWE-119: https://cwe.mitre.org/data/definitions/119.html
  3. CERT C Secure Coding Standard: https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+Standard
  4. Microsoft Secure C/C++ Guidelines: https://learn.microsoft.com/en-us/cpp/security/security-best-practices-for-c-cpp
  5. LLVM AddressSanitizer: https://clang.llvm.org/docs/AddressSanitizer.html
  6. OWASP IoTGoat (Buffer Overflow Labs): https://github.com/OWASP/IoTGoat

This paper is part of an ongoing initiative to enhance security awareness and defensive programming practices among embedded software developers. The insights presented here reflect field experience and continuing research in memory safety within low-level systems.

Latest Articles

Cybersecurity Training for Enterprises

Build resilience in your workforce with industry-focused,hands-on, practical cybersecurity programs.

Contact Us
hero-image