Back to Blog
critical SEVERITY8 min read

How buffer overflow happens in C ieee80211_input() and how to fix it

A critical buffer overflow vulnerability was discovered in `src/firmware/src/net/ieee80211.c` at line 1584, where the `ieee80211_input()` function processed raw 802.11 data frames without verifying that the incoming frame was large enough to contain a valid `ieee80211_frame` header. An attacker within wireless range could craft undersized or malformed frames to trigger memory corruption, potentially leading to remote code execution on the firmware. The fix adds a single, targeted bounds check th

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published July 13, 2026•Reviewed July 13, 2026

Answer Summary

This is a buffer overflow vulnerability (CWE-120) in C firmware code, specifically in the `ieee80211_input()` function in `src/firmware/src/net/ieee80211.c`. The function accepted raw 802.11 data frames of arbitrary length and passed them to downstream processing functions without first verifying the frame was at least as large as a valid `struct ieee80211_frame` header. The fix adds a length guard — `if(length < sizeof(struct ieee80211_frame)) return;` — immediately before the call to `ieee80211_input_data()`, ensuring undersized frames are discarded before any memory access occurs.

Vulnerability at a Glance

cweCWE-120
fixAdded `if(length < sizeof(struct ieee80211_frame)) return;` before the `ieee80211_input_data()` call at line 1606
riskRemote memory corruption via crafted 802.11 wireless frames, potentially leading to arbitrary code execution on the firmware
languageC
root cause`ieee80211_input()` forwarded DATA-type frames to `ieee80211_input_data()` without checking that `length >= sizeof(struct ieee80211_frame)`
vulnerabilityBuffer Overflow (missing frame-length bounds check)

How Buffer Overflow Happens in C ieee80211_input() and How to Fix It

Summary

A critical buffer overflow vulnerability was discovered in src/firmware/src/net/ieee80211.c, where the ieee80211_input() function processed raw 802.11 data frames without first verifying that the incoming frame was large enough to contain a valid ieee80211_frame header. An attacker within wireless range could craft undersized or malformed frames to trigger memory corruption in the firmware — potentially enabling remote code execution. The fix is a single, targeted bounds check inserted before the vulnerable call to ieee80211_input_data().


Introduction

The src/firmware/src/net/ieee80211.c file sits at the heart of a device's wireless stack. It is responsible for receiving raw 802.11 frames off the air and routing them to the appropriate handler based on frame type: management, control, or data. That routing logic lives in ieee80211_input(), and until this fix, it had a dangerous blind spot.

When a data frame arrived (IEEE80211_FC0_TYPE_DATA), the function immediately forwarded it to ieee80211_input_data(frame, length, rssi) — no questions asked. There was no check that length was actually large enough to hold a struct ieee80211_frame header. Any code inside ieee80211_input_data() that reads fields from the frame header was doing so on the assumption that the frame was well-formed. An attacker who could inject a frame shorter than sizeof(struct ieee80211_frame) bytes could violate that assumption, causing the firmware to read (or write) memory it was never supposed to touch.

This matters to developers working on embedded networking code because the pattern is easy to introduce: you receive a uint8_t *frame with an accompanying uint32_t length, and it feels natural to trust that the caller has already validated the size. In a raw wireless receive path, no one has.


The Vulnerability Explained

The Vulnerable Code

Before the fix, the data-frame branch of ieee80211_input() looked like this (around line 1602–1606):

case IEEE80211_FC0_TYPE_DATA:
    ieee80211_input_data(frame, length, rssi);
    break;

The function signature for ieee80211_input is:

void ieee80211_input(uint8_t *frame, uint32_t length, int16_t rssi)

Both frame and length come directly from the wireless hardware receive buffer — they are fully attacker-controlled. The length field reflects whatever was encoded in the received frame, and there is no prior sanitization step before ieee80211_input() is called.

Why This Is Dangerous

ieee80211_input_data() will immediately begin parsing frame as a struct ieee80211_frame, accessing fields like the address fields (i_addr1, i_addr2, i_addr3), the sequence control field, and potentially QoS or HT control fields depending on the frame subtype. Each of those accesses is a pointer dereference into frame at a fixed offset.

If length is, say, 4 bytes — far smaller than sizeof(struct ieee80211_frame) (typically 24 bytes for a basic data frame) — then ieee80211_input_data() will read bytes beyond the end of the allocated frame buffer when it tries to access later fields. Depending on memory layout, this can:

  • Leak sensitive data from adjacent memory regions (e.g., cryptographic keys, session state)
  • Corrupt memory if any write operations follow the bad read
  • Crash the firmware (denial of service) if the out-of-bounds access hits unmapped memory
  • Enable code execution if an attacker can carefully control what lies beyond the buffer

Attack Scenario

An attacker within 802.11 radio range of the target device crafts a raw data frame with a valid 802.11 frame control field (FC0_TYPE_DATA) but a total length of only 6 bytes — just enough to pass the frame-type check, but far too short to contain a full ieee80211_frame header. They inject this frame using a monitor-mode wireless adapter and a tool like scapy or a custom kernel module.

The firmware's receive interrupt fires, calls ieee80211_input(frame, 6, rssi), enters the IEEE80211_FC0_TYPE_DATA case, and immediately calls ieee80211_input_data(frame, 6, rssi). Inside that function, code reads frame->i_addr2 at byte offset 10 — 4 bytes past the end of the 6-byte buffer. The attacker repeats this with varying lengths and frame content to probe memory layout, then crafts a payload that overwrites a function pointer or return address.

This attack requires no authentication, no association, and no prior knowledge of the device beyond its presence on the air.


The Fix

What Changed

A single line was added to the IEEE80211_FC0_TYPE_DATA case in ieee80211_input(), at line 1606:

case IEEE80211_FC0_TYPE_DATA:
+   if(length < sizeof(struct ieee80211_frame)) return;
    ieee80211_input_data(frame, length, rssi);
    break;

Before vs. After

Before (vulnerable):

case IEEE80211_FC0_TYPE_DATA:
    ieee80211_input_data(frame, length, rssi);
    break;

After (fixed):

case IEEE80211_FC0_TYPE_DATA:
    if(length < sizeof(struct ieee80211_frame)) return;
    ieee80211_input_data(frame, length, rssi);
    break;

Why This Fix Works

sizeof(struct ieee80211_frame) is a compile-time constant that gives the exact number of bytes needed to represent a minimal, well-formed 802.11 frame header. By comparing length against this value before calling ieee80211_input_data(), the code guarantees that any downstream access to header fields within the first sizeof(struct ieee80211_frame) bytes is safe.

If the frame is too short, the function returns immediately — no memory is accessed, no state is corrupted, and the malformed frame is silently discarded. This is the correct behavior for a firmware receive path: drop what you cannot safely process.

The fix is minimal and targeted. It does not change the logic for well-formed frames, does not introduce new dependencies, and adds negligible overhead (a single integer comparison per data frame).

Note for maintainers: Similar length checks should be audited for the IEEE80211_FC0_TYPE_MGT and IEEE80211_FC0_TYPE_CTL branches. The fix addresses the data-frame path, but management and control frame handlers (ieee80211_input_ctl, and any management frame dispatcher) should be reviewed to confirm they perform equivalent minimum-length validation before header field access.


Key Takeaways

  • Never assume a received wireless frame meets a minimum size. The ieee80211_input() function accepted length directly from hardware without any prior validation — always check before you parse.
  • sizeof(struct ieee80211_frame) is your minimum gate for data frames. Any code path that dereferences a frame pointer must be preceded by this comparison.
  • A single missing comparison can expose an entire device to remote attack. The vulnerable code path required zero authentication — just proximity to the device's wireless interface.
  • The fix is one line, but the audit should be broader. The control and management frame branches in the same switch statement deserve the same scrutiny applied here to the data frame branch.
  • Firmware buffer overflows are especially severe. Unlike application-layer bugs, there is no OS sandbox, ASLR, or stack canary to rely on in many embedded environments — making the absence of input validation directly exploitable.

How Orbis AppSec Detected This

  • Source: Raw 802.11 frame data received from the wireless hardware interface, passed as uint8_t *frame with attacker-controlled uint32_t length into ieee80211_input().
  • Sink: ieee80211_input_data(frame, length, rssi) at src/firmware/src/net/ieee80211.c:1605, which dereferences frame as a struct ieee80211_frame * without any prior length validation.
  • Missing control: No check that length >= sizeof(struct ieee80211_frame) before forwarding to ieee80211_input_data().
  • CWE: CWE-120 — Buffer Copy without Checking Size of Input ("Classic Buffer Overflow").
  • Fix: Inserted if(length < sizeof(struct ieee80211_frame)) return; immediately before the ieee80211_input_data() call, blocking all undersized frames from reaching the vulnerable parsing logic.

Orbis AppSec detects issues like this automatically. Try Orbis AppSec on your repositories to find and fix issues like this automatically.


Conclusion

The vulnerability in ieee80211_input() is a textbook example of how a single missing bounds check in a binary protocol parser can open a critical attack surface — in this case, one reachable by any attacker within wireless range, requiring no credentials and no prior interaction with the device. The fix is elegantly simple: one comparison, if(length < sizeof(struct ieee80211_frame)) return;, inserted at exactly the right point in the data-frame processing path.

For developers working on firmware, drivers, or any C code that processes externally-sourced binary data: make length validation the first thing you write, not an afterthought. Treat every uint8_t *buf, size_t len pair as hostile until proven otherwise, and always use sizeof() of your target structure as the minimum acceptable length before you touch a single field.


Prevention and further reading

Related Articles

critical

Aardvark.Cef.Process.Core Shared Memory Handler: Unvalidated memcpy

The shared memory handler in Aardvark.Cef.Process.Core failed to validate that the `length` parameter passed from JavaScript did not exceed the destination buffer size before calling `memcpy`. This allowed a compromised Chromium renderer process to corrupt heap memory by supplying an oversized `handle->length` value to the `openMapping` function. The fix adds explicit bounds checking using `GetArrayBufferByteLength()` before the copy operation.

high

XShmGetImage Heap Corruption: Unvalidated Image Height in streamproxy

The `XShmGetImage` function in the X11 shared memory image path copies pixel data row-by-row using `memcpy` without validating that the source image height matches the destination buffer height. An attacker or compromised server could provide an oversized source image, causing writes beyond the allocated heap buffer and triggering heap corruption or code execution.

critical

SGX Enclave ecall_store_data memcpy Buffer Overflow in 256-Byte

The Intel SGX enclave's trusted bridge functions `ecall_store_data` and `ecall_retrieve_data` used `memcpy()` to move data into and out of a fixed 256-byte `secure_storage` buffer without validating that `data_len` fit within destination boundaries. An attacker providing oversized `data_len` values could corrupt enclave memory, breaking SGX's confidentiality guarantees.

critical

How Buffer Overflow via strcpy() Happens in C++ XML Parsers and How to Fix It

A critical buffer overflow vulnerability was discovered in `buildroot-external/package/libxmlparser/xmlParser.cpp`, where the `toXMLString` function used `_tcscpy()` to write XML escape sequences into a destination buffer without any bounds checking. An attacker supplying a crafted XML document could overflow the buffer and potentially execute arbitrary code. The fix replaces all five unsafe `_tcscpy()` calls with `memcpy()` calls that copy only the exact number of bytes required for each escape

critical

How Heap Buffer Overflows Happen in C++ ZIP Extraction and How to Fix Them

A critical heap buffer overflow vulnerability was discovered in `TKLiveSync/unzip.cpp`, where ZIP archive entry names were copied into a `PATH_MAX`-sized heap buffer using `strcpy()` without any length validation. Since the ZIP specification allows entry names up to 65,535 bytes — far exceeding typical `PATH_MAX` values of 1,024 to 4,096 bytes — a crafted archive could overflow the buffer and corrupt heap memory. The fix replaces the unsafe `strcpy`/`dirname` pattern with `std::string` operation

medium

How Integer Overflow happens in C++ image processing and how to fix it

A signed integer overflow in OpenCV's `bilateralFilter.cpp` allowed the buffer size calculation `cal_width * cal_height * cn` to wrap around to a small or negative value, causing `padding.resize()` to allocate far less memory than needed. Subsequent `memcpy` operations would then write beyond the allocated buffer, creating a heap corruption primitive. The fix is a single targeted cast to `size_t` that promotes the multiplication to unsigned 64-bit arithmetic before any overflow can occur.