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
Published July 13, 2026Reviewed 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.


Prevention & Best Practices

1. Always Validate Frame/Packet Length Before Header Access

In any C code that processes binary protocol data, establish a pattern: check length before you dereference. This applies to every protocol layer — 802.11, Ethernet, IP, TCP, TLS record parsing, and custom binary protocols alike.

/* Pattern: always gate header access with a length check */
if (length < sizeof(struct my_protocol_header)) {
    return -EINVAL; /* or return; for void functions */
}
struct my_protocol_header *hdr = (struct my_protocol_header *)buf;
/* Now safe to access hdr->fields */

2. Use sizeof() of the Target Structure, Not Magic Numbers

Hardcoded size constants (e.g., if (length < 24)) are fragile — they break silently if the structure definition changes. Using sizeof(struct ieee80211_frame) ties the check directly to the structure and will automatically update if the struct is modified.

3. Check Incrementally for Variable-Length Headers

802.11 frames have variable-length headers (QoS data adds 2 bytes, HT control adds 4 more, etc.). After the initial sizeof(struct ieee80211_frame) check, add secondary checks before accessing optional fields:

if (length < sizeof(struct ieee80211_frame)) return;

struct ieee80211_frame *wh = (struct ieee80211_frame *)frame;
if (IEEE80211_QOS_HAS_SEQ(wh)) {
    if (length < sizeof(struct ieee80211_qosframe)) return;
}

4. Use Static Analysis in Your CI Pipeline

Tools that can catch this class of issue include:

  • Orbis AppSec — detected this exact vulnerability automatically
  • Coverity — tracks tainted length values through call chains
  • CodeQL — can model buffer access patterns in C/C++
  • Semgrep — custom rules for protocol parsing patterns

5. Fuzz the Wireless Receive Path

Fuzz testing is particularly effective for frame-parsing code. Tools like libFuzzer or AFL++ can be pointed at ieee80211_input() with a corpus of valid 802.11 frames and will quickly generate undersized and malformed inputs that expose missing bounds checks.

6. Reference Standards


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 automatically detected this vulnerability and opened a pull request with the fix. 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.


References

Frequently Asked Questions

What is a buffer overflow in wireless firmware?

A buffer overflow occurs when code reads or writes beyond the boundaries of an allocated memory buffer. In wireless firmware, this can happen when a frame-processing function assumes an incoming packet meets a minimum size without verifying it, allowing an attacker to craft a short or malformed frame that causes the code to access memory outside the intended buffer.

How do you prevent buffer overflows in C wireless drivers?

Always validate that received data is at least as large as the structure you intend to interpret it as before dereferencing any pointers into it. For 802.11 frame processing, check `length >= sizeof(struct ieee80211_frame)` (and any additional subtype-specific header sizes) before passing the frame to downstream functions.

What CWE is a buffer overflow?

Classic buffer overflows are classified as CWE-120 ("Buffer Copy without Checking Size of Input"). Related entries include CWE-125 (Out-of-bounds Read) and CWE-787 (Out-of-bounds Write), depending on whether the overflow involves reading, writing, or both.

Is input sanitization at the network edge enough to prevent this?

No. Network-edge filtering (e.g., firewall rules) cannot reliably block malformed 802.11 frames because the vulnerability exists in the firmware's own wireless receive path, which processes frames before any higher-layer filtering can occur. The fix must be in the frame-processing code itself.

Can static analysis detect missing bounds checks like this?

Yes. Static analysis tools — including AI-assisted scanners like Orbis AppSec — can identify code paths where a length parameter is accepted from an external source but never compared against a minimum structure size before being used in a downstream function call.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #189

Related Articles

critical

How buffer overflow happens in C tar header parsing and how to fix it

A critical buffer overflow vulnerability was discovered in `microtar/microtar.c` where the `raw_to_header()` and `header_to_raw()` functions used unbounded `strcpy()` and `sprintf()` calls to copy tar header fields. Malicious tar files with non-null-terminated name fields could overflow destination buffers, potentially leading to code execution. The fix replaces all unsafe string operations with bounded alternatives: `memcpy()` with explicit null-termination and `snprintf()` instead of `sprintf(

high

How buffer overflow from unsafe string copy functions happens in C network interface code and how to fix it

A high-severity buffer overflow vulnerability was discovered in `generic/eth-impl.c`, where unsafe `strncpy()` and `sprintf()` calls could write beyond buffer boundaries when handling network interface names and device filenames. The fix replaced these dangerous functions with bounded `snprintf()` calls that guarantee null-termination and prevent memory corruption.

critical

How buffer overflow in FuzzIxml.c sprintf() happens in C and how to fix it

A critical buffer overflow vulnerability was discovered in `fuzzer/FuzzIxml.c` where `sprintf()` wrote a PID-formatted filename into a fixed 256-byte stack buffer without any bounds checking. The fix replaces `sprintf()` with `snprintf()`, explicitly passing the buffer size to prevent any overflow. While exploitation in this specific fuzzer context requires local access, the pattern is a textbook example of CWE-120 that developers should recognize and eliminate everywhere it appears.

critical

How buffer overflow happens in C HTML parsing and how to fix it

A critical buffer overflow vulnerability in `include/html_parse.h` allowed attackers to overflow buffers by providing malicious HTML input exceeding buffer capacity. The fix adds proper bounds checking before memcpy() operations to prevent memory corruption and potential code execution.

critical

How buffer overflow in memcpy() happens in Node.js N-API bindings and how to fix it

A critical buffer overflow vulnerability was discovered in the GetBufferAsVector() function in examples_nodejs/src/zupt_napi.cpp, where memcpy() copied data from JavaScript Uint8Array buffers without proper bounds validation. This vulnerability could allow attackers to trigger memory corruption by providing maliciously crafted input arrays to the native Node.js module, potentially leading to crashes or arbitrary code execution.

critical

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

A critical buffer overflow vulnerability was discovered in the ArrowTest() function in main/main.c, where sprintf() was writing formatted strings to a 24-byte buffer without bounds checking. By replacing sprintf() with snprintf() and specifying the buffer size, the vulnerability was eliminated, preventing attackers from corrupting heap memory through oversized width or height parameters.