Back to Blog
high SEVERITY8 min read

When Network Frames Attack: Fixing a Heap Buffer Overflow in lwIP's Hosted Driver

A critical heap buffer overflow vulnerability was discovered in the `lwip_hosted_rx_input` function of the lwIP hosted wireless driver, where raw network frames from an ESP-hosted interface were copied into packet buffers without validating the frame length against the buffer's maximum capacity. Because `pbuf_alloc` uses an unsigned 16-bit integer for size, an attacker on the same wireless network could craft a packet with an oversized length field, causing silent integer truncation and subseque

O
By Orbis AppSec
Published May 28, 2026Reviewed June 3, 2026

Answer Summary

This is a heap buffer overflow vulnerability (CWE-122) in the lwIP hosted wireless driver written in C. The `lwip_hosted_rx_input` function copied network frames without validating frame length against the 16-bit maximum capacity of `pbuf_alloc`, allowing integer truncation. The fix adds explicit bounds checking to reject frames exceeding `UINT16_MAX` before allocation.

Vulnerability at a Glance

cweCWE-122
fixAdd bounds check ensuring frame length fits within uint16_t before allocation
riskRemote code execution from same wireless network
languageC
root causeMissing frame length validation before 16-bit pbuf_alloc call
vulnerabilityHeap Buffer Overflow via Integer Truncation

When Network Frames Attack: Fixing a Heap Buffer Overflow in lwIP's Hosted Driver

Introduction

Embedded networking stacks sit at a fascinating — and dangerous — intersection: they must be lean enough to run on microcontrollers with kilobytes of RAM, yet robust enough to handle untrusted input arriving over the air from any device within wireless range. When a validation step is missing at that boundary, the consequences can be severe.

This post examines a critical heap buffer overflow found in drivers/lwip_hosted/lwip_hosted.c, the glue layer between the ESP-hosted wireless interface and the lwIP TCP/IP stack. The root cause is deceptively simple: a frame length value arriving from the network was never checked against the maximum size that the packet buffer allocator could safely handle. One crafted packet from a nearby attacker was enough to corrupt heap memory — and potentially achieve arbitrary code execution on the target device.

Who should read this? Embedded developers working with lwIP, ESP-IDF, or any network driver that bridges raw frames into a managed buffer system. The pattern is common; the lesson is universal.


The Vulnerability Explained

Architecture Background

The lwip_hosted driver acts as a network interface (netif) driver for lwIP when the wireless connectivity is provided by a co-processor running the ESP-hosted firmware. The data path looks like this:

Wireless co-processor
        │
        │  (raw frame + esp_hosted_frame_info_t metadata)
        ▼
lwip_hosted_rx_input()          ← vulnerability lives here
        │
        │  pbuf_alloc() + memcpy()
        ▼
lwIP network stack

When a frame arrives, lwip_hosted_rx_input is called with:
- info — a pointer to esp_hosted_frame_info_t containing frame metadata
- payload — a pointer to the raw frame bytes
- len — the reported length of the frame (a size_t, i.e., 32- or 64-bit)

The Dangerous Code Path

Before the fix, the function validated only the obvious error conditions:

// BEFORE — vulnerable code
if (!s_lwip.initialized || info == NULL || payload == NULL || len == 0U)
{
    return;
}

After passing these checks, the code called pbuf_alloc to allocate a packet buffer and then copied len bytes into it. Here is the critical problem:

pbuf_alloc accepts a u16_t (unsigned 16-bit integer) for the size parameter. If len is larger than 65535 (the maximum value of a u16_t), the value is silently truncated when passed to pbuf_alloc. The allocator happily creates a small buffer — say, a buffer for the truncated size — while the subsequent memcpy still uses the original, full-size len value.

The result: more bytes are written into the buffer than the buffer can hold, overflowing into adjacent heap memory.

Integer Truncation: A Visual Breakdown

Attacker sends frame with len = 0x00010010 (65,552 bytes)
                                    
                                    
pbuf_alloc((u16_t)len)    pbuf_alloc(0x0010)    allocates 16-byte buffer
                                    
memcpy(pbuf->payload, frame, len)   
       └──────────────────────────► copies 65,552 bytes into 16-byte buffer
                                    
                                    
                           ████ HEAP CORRUPTION ████

This is a classic CWE-122: Heap-based Buffer Overflow triggered via CWE-190: Integer Overflow or Wraparound (specifically, truncation on implicit narrowing conversion).

Real-World Exploitability

This vulnerability is remotely exploitable by any attacker on the same wireless network segment — no authentication required. An attacker needs only to:

  1. Connect to (or be present on) the same Wi-Fi network or wireless segment as the target device.
  2. Craft a raw network frame where the length field in esp_hosted_frame_info_t is set to a value greater than 65535.
  3. Transmit the frame.

The heap corruption that follows can be leveraged for:

  • Denial of Service — crashing the device by corrupting heap metadata or critical data structures.
  • Information Disclosure — overwriting memory in ways that cause sensitive data to be returned or logged.
  • Remote Code Execution — with sufficient control over heap layout, an attacker may overwrite function pointers or return addresses, redirecting execution flow.

On resource-constrained embedded targets, heap layout is often predictable, making exploitation more reliable than on desktop operating systems with ASLR and heap hardening.


The Fix

What Changed

The fix adds a single, targeted bounds check immediately after the existing input validation:

// AFTER — patched code
if (!s_lwip.initialized || info == NULL || payload == NULL || len == 0U ||
    len > UINT16_MAX) /* pbuf_alloc takes u16_t; prevent silent truncation */
{
    return;
}

Why This Works

By rejecting any len value that exceeds UINT16_MAX (65,535), the fix guarantees that the subsequent cast from size_t to u16_t is lossless. The allocated pbuf will always be large enough to hold exactly len bytes, eliminating the overflow condition entirely.

The fix is minimal and surgical — it does not change the happy path for valid frames, does not introduce new data structures, and adds negligible runtime overhead (a single integer comparison).

Before vs. After

Scenario Before Fix After Fix
len = 1000 (normal frame) ✅ Processed correctly ✅ Processed correctly
len = 65535 (max valid) ✅ Processed correctly ✅ Processed correctly
len = 65536 (one over) ❌ Buffer overflow ✅ Silently dropped
len = 0x00010010 (crafted) ❌ Heap corruption ✅ Silently dropped

The Comment Matters

Notice that the patch includes an inline comment:

len > UINT16_MAX) /* pbuf_alloc takes u16_t; prevent silent truncation */

This is not cosmetic. It explains why the check exists — a future maintainer who doesn't know the pbuf_alloc signature might otherwise consider this check redundant and remove it. Good security fixes document their reasoning.


Prevention & Best Practices

1. Treat Every API Type Boundary as a Security Boundary

Whenever data crosses from a wider type to a narrower type — especially at network input paths — explicitly validate that the value fits. Don't rely on the compiler to warn you; implicit narrowing conversions are legal C and C++ and compilers may only emit warnings at higher warning levels.

// Pattern: always validate before narrowing
assert(len <= UINT16_MAX);          // debug builds
if (len > UINT16_MAX) return ERR;   // release builds
u16_t safe_len = (u16_t)len;

2. Enable Compiler Warnings for Implicit Conversions

Compilers can catch many of these issues at build time:

# CMake / GCC / Clang
target_compile_options(your_target PRIVATE
    -Wconversion          # warn on implicit type conversions
    -Wsign-conversion     # warn on signed/unsigned mismatches
    -Wextra
    -Werror               # treat warnings as errors in CI
)

3. Use Static Analysis Tools

Static analyzers excel at finding type truncation and buffer size mismatches:

Tool What It Catches
Coverity Integer truncation, buffer overflows
CodeChecker / clang-tidy Implicit narrowing, unsafe casts
PVS-Studio Width-changing integer conversions
Codesonar Heap buffer overflows in C/C++

Many of these can be integrated directly into CI/CD pipelines to catch regressions before they reach production.

4. Apply Defense in Depth at Network Input Points

For any function that receives raw network data, apply a consistent validation checklist:

// Network input validation checklist
static esp_err_t validate_frame(const void *payload, size_t len)
{
    if (payload == NULL)           return ERR_ARG;   // null check
    if (len == 0)                  return ERR_ARG;   // empty frame
    if (len > MAX_FRAME_SIZE)      return ERR_ARG;   // upper bound
    if (len > UINT16_MAX)          return ERR_ARG;   // API type limit
    if (len < MIN_ETHERNET_FRAME)  return ERR_ARG;   // lower bound
    return ERR_OK;
}

5. Know Your lwIP API Contracts

lwIP's pbuf_alloc signature is:

struct pbuf *pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type);

The u16_t length parameter is a well-known constraint in lwIP development. Any driver code that feeds externally-sourced lengths into this function must validate against UINT16_MAX before the call. This should be part of your team's lwIP integration checklist.

6. Relevant Standards and References

  • CWE-122: Heap-based Buffer Overflow — https://cwe.mitre.org/data/definitions/122.html
  • CWE-190: Integer Overflow or Wraparound — https://cwe.mitre.org/data/definitions/190.html
  • CWE-20: Improper Input Validation — https://cwe.mitre.org/data/definitions/20.html
  • OWASP: Buffer Overflow — https://owasp.org/www-community/vulnerabilities/Buffer_Overflow
  • CERT C Rule INT31-C: Ensure that integer conversions do not result in lost or misinterpreted data

Conclusion

This vulnerability is a textbook example of how a missing bounds check at a type boundary can turn routine network processing into a critical security flaw. The attack surface is wide — any device on the wireless network — and the potential impact reaches all the way to remote code execution.

The fix is elegant in its simplicity: one comparison, one comment, one early return. But finding it requires understanding the full data flow from the wireless co-processor through the driver into the lwIP allocator, and recognizing that size_t and u16_t are not interchangeable.

Key Takeaways

  • 🔴 Silent integer truncation is dangerous — always validate before narrowing a type at security-sensitive boundaries.
  • 🟡 Network input is untrusted input — apply the same rigor to incoming frames as you would to user-supplied form data.
  • 🟢 One line can fix a critical vulnerability — but only if you understand why it's needed. Document your intent.
  • 🔵 Enable -Wconversion in your builds — let the compiler be your first line of defense.

Embedded systems are increasingly connected, and increasingly targeted. Building the habit of validating every external input — especially at the network boundary — is one of the highest-leverage security investments a firmware team can make.


This vulnerability was identified and fixed by automated security scanning. Automated tools can catch entire classes of vulnerabilities before they ship — consider integrating security scanning into your CI/CD pipeline.

Frequently Asked Questions

What is a heap buffer overflow?

A heap buffer overflow occurs when a program writes data beyond the allocated memory boundary on the heap, potentially corrupting adjacent memory structures and enabling code execution.

How do you prevent heap buffer overflow in C?

Always validate input sizes before memory operations, use safe memory functions with explicit bounds, and ensure size calculations cannot overflow or truncate when cast to smaller integer types.

What CWE is heap buffer overflow?

CWE-122 (Heap-based Buffer Overflow) covers this vulnerability class, with CWE-190 (Integer Overflow or Wraparound) often being the root cause.

Is using pbuf_alloc enough to prevent buffer overflows?

No, pbuf_alloc only allocates the requested size. If the size parameter is truncated due to integer overflow, the allocated buffer will be smaller than expected, leading to overflow during copy operations.

Can static analysis detect heap buffer overflow?

Yes, static analysis tools can detect many heap buffer overflows, especially those involving obvious integer truncation patterns or missing bounds checks before memory operations.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #2

Related Articles

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

high

How insecure string copy functions happen in C and how to fix it

A high-severity buffer overflow vulnerability was discovered in `tools/claude-vscode-wrapper.c`, where an unbounded `strcpy()` call copied a file path into a fixed-size `MAX_PATH` buffer without any size validation. The fix replaces `strcpy()` with `snprintf()` and swaps `malloc()` for `calloc()`, ensuring both string operations and memory allocation are bounds-safe and zero-initialized.

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.

critical

How Stack Buffer Overflows Happen in C with sprintf() and How to Fix Them

A critical stack buffer overflow was discovered in `libuv/Learn-libuv/docs/code/tty-gravity/main.c` where `sprintf()` wrote ANSI escape sequences and user-controlled variables into a fixed 500-byte buffer without any bounds checking. An attacker controlling the `pos`, `width`, or `message` variables could overflow the stack, overwrite return addresses, and potentially achieve arbitrary code execution. The fix replaces `sprintf()` with `snprintf()` and adds explicit length validation to ensure wr

high

How insecure string copy functions happen in C and how to fix them

A high-severity buffer overflow risk was discovered in `login/main.c` where `strcpy()` was used to copy the `HOME` environment variable into a fixed-size 512-byte buffer without any bounds checking. An attacker controlling the `HOME` environment variable could overflow `pwd_file_name`, potentially corrupting memory or hijacking execution. The fix replaces the two-step `strcpy`/`strcat` pattern with a single, bounds-safe `snprintf` call.