Back to Blog
critical SEVERITY8 min read

Heap Buffer Overflow in tzsp_forwarder.c: When Packets Attack

A critical heap buffer overflow vulnerability (CWE-120) was discovered and patched in `contrib/tzsp_forwarder.c`, where an attacker-controlled `caplen` value from a crafted network packet could overwrite adjacent heap memory structures. This class of vulnerability can lead to remote code execution, process crashes, or sensitive data disclosure. The fix introduces proper bounds validation before the dangerous `memcpy` operation, closing the door on this attack vector.

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

Answer Summary

A heap buffer overflow (CWE-120) in C occurs when a `memcpy()` operation copies more data than the destination buffer can hold, often due to attacker-controlled size parameters. In this case, the `tzsp_forwarder.c` file accepted a `caplen` value from network packets without validation before copying data. The fix adds bounds checking to ensure `caplen` never exceeds the allocated buffer size before the `memcpy` call, preventing heap corruption.

Vulnerability at a Glance

cweCWE-120
fixAdd bounds checking to validate `caplen` against buffer size before memory copy
riskRemote code execution, process crash, or data disclosure via crafted network packets
languageC
root causeMissing bounds validation on attacker-controlled `caplen` before `memcpy`
vulnerabilityHeap Buffer Overflow

Heap Buffer Overflow in tzsp_forwarder.c: When Network Packets Attack Your Heap

Introduction

What if the data you were processing could secretly rewrite your program's memory? That's precisely what a heap buffer overflow allows — and it's one of the most dangerous vulnerability classes in systems programming. This week, a critical heap buffer overflow (CWE-120) was patched in contrib/tzsp_forwarder.c, a C component responsible for forwarding TZSP (TaZmen Sniffer Protocol) encapsulated network packets.

If you write C or C++ code that processes network data — or if you depend on software that does — this vulnerability and its fix are a masterclass in why never trusting attacker-controlled input is a foundational security principle.


Background: What is TZSP?

TZSP (TaZmen Sniffer Protocol) is a lightweight encapsulation protocol used to tunnel captured network packets from one device to another. It's commonly found in network monitoring equipment, wireless access points, and packet capture tools. A TZSP forwarder receives raw captured packets and re-encapsulates or relays them.

Because TZSP forwarders sit at the boundary between untrusted network data and internal processing logic, they are high-value targets for attackers. Any bug in how packet metadata is handled can have severe consequences.


The Vulnerability Explained

What Went Wrong

At line 53 of contrib/tzsp_forwarder.c, a memcpy operation copies data from a captured network packet into a destination buffer:

// VULNERABLE CODE (simplified)
memcpy(tzsp_packet + offset, captured_data, caplen);

The critical problem: caplen comes directly from the captured packet header — data that is entirely under the attacker's control. Before this fix, there was no validation to ensure that caplen bytes would actually fit into the remaining space of tzsp_packet (i.e., the allocated buffer size minus offset).

The Anatomy of the Attack

Here's what the vulnerable logic looked like conceptually:

// BEFORE: No bounds checking — DANGEROUS
uint8_t *tzsp_packet = malloc(TZSP_BUFFER_SIZE);
size_t offset = build_tzsp_header(tzsp_packet);  // some header bytes written

// caplen is read directly from the attacker-supplied packet header
uint32_t caplen = ntohl(pcap_header->caplen);

// If caplen > (TZSP_BUFFER_SIZE - offset), we overflow the heap!
memcpy(tzsp_packet + offset, packet_data, caplen);

An attacker crafts a malicious packet where the caplen field is set to a value larger than TZSP_BUFFER_SIZE - offset. When memcpy executes, it writes beyond the end of tzsp_packet, overwriting adjacent heap memory structures.

What Can an Attacker Actually Do?

Heap buffer overflows are notoriously powerful primitives for attackers. Depending on the heap layout and the target environment, exploitation can lead to:

Impact Description
Remote Code Execution (RCE) Overwriting heap metadata or function pointers to redirect execution flow
Process Crash / DoS Corrupting heap structures causes malloc/free to abort the process
Sensitive Data Disclosure Overwriting adjacent buffers containing credentials, keys, or private data
Privilege Escalation If the forwarder runs as root (common for raw socket access), RCE = full system compromise

Real-World Attack Scenario

Imagine a network monitoring appliance running this TZSP forwarder daemon with root privileges (required to open raw sockets). The appliance is connected to a mirrored switch port and receives all network traffic.

  1. Attacker sends a crafted UDP packet to the TZSP listener port with caplen set to 0xFFFFFFFF (4,294,967,295 bytes).
  2. The forwarder reads this value directly from the packet header without validation.
  3. memcpy begins writing ~4GB of data starting at tzsp_packet + offset.
  4. Within the first few kilobytes, it overwrites the metadata of adjacent heap chunks.
  5. On the next malloc or free call, the heap allocator reads corrupted metadata, potentially jumping to an attacker-controlled address.
  6. Game over — the attacker has code execution as root on the monitoring appliance, with visibility into all mirrored network traffic.

This isn't a theoretical scenario. CVE databases are full of real-world exploits following exactly this pattern.


The Fix

What Changed

The fix introduces bounds validation before the memcpy call. The corrected logic ensures that caplen cannot exceed the remaining available space in the destination buffer:

// AFTER: Bounds checking added — SAFE
uint8_t *tzsp_packet = malloc(TZSP_BUFFER_SIZE);
size_t offset = build_tzsp_header(tzsp_packet);

// caplen is read from the attacker-supplied packet header
uint32_t caplen = ntohl(pcap_header->caplen);

// Validate: ensure caplen fits within the remaining buffer space
if (caplen > TZSP_BUFFER_SIZE - offset) {
    // Log the anomaly and discard the malformed packet
    log_warning("Dropping packet: caplen %u exceeds buffer bounds", caplen);
    free(tzsp_packet);
    return;
}

// Now safe to copy
memcpy(tzsp_packet + offset, packet_data, caplen);

Why This Fix Works

The fix enforces a hard upper bound on caplen before it's used as the length argument to memcpy. No matter what value an attacker puts in the caplen field, the code will never write beyond the allocated buffer.

Key elements of a good fix here:

  1. Explicit upper bound check: caplen > TZSP_BUFFER_SIZE - offset catches the overflow condition. Note the subtraction order matters — subtracting offset from TZSP_BUFFER_SIZE (not the other way around) avoids an integer underflow if offset > TZSP_BUFFER_SIZE.

  2. Graceful rejection: Instead of truncating silently (which could cause other bugs), the malformed packet is logged and discarded. This also aids in detecting attack attempts.

  3. No silent truncation: A common mistake is to "fix" this by clamping: caplen = MIN(caplen, remaining). While this prevents the overflow, it silently processes a truncated packet, which may cause protocol-level issues downstream. Dropping the packet entirely is safer.

Integer Overflow Consideration

One subtle trap: if offset is larger than TZSP_BUFFER_SIZE (which shouldn't happen but could under other bugs), the subtraction TZSP_BUFFER_SIZE - offset wraps around to a huge number, making the check useless. A fully hardened version adds a secondary guard:

// Belt-and-suspenders: also guard against offset corruption
if (offset >= TZSP_BUFFER_SIZE || caplen > TZSP_BUFFER_SIZE - offset) {
    log_warning("Dropping packet: invalid offset or caplen");
    free(tzsp_packet);
    return;
}

Prevention & Best Practices

1. Never Trust Attacker-Controlled Length Fields

Any length, size, or count value that originates from network data, user input, or untrusted files must be validated before use in memory operations. This is the cardinal rule of safe C programming.

// ALWAYS validate before using external lengths
if (external_length > sizeof(buffer) - current_offset) {
    handle_error();
    return;
}
memcpy(buffer + current_offset, source, external_length);

2. Use Safe Memory Functions Where Available

Modern C libraries provide safer alternatives:

// Prefer memcpy_s (C11 Annex K) where available
errno_t err = memcpy_s(dest, dest_size, src, count);
if (err != 0) { /* handle error */ }

Or use wrappers that enforce bounds:

// OpenBSD-style: returns 0 on success, -1 on overflow
if (buf_append(&buf, src, caplen) < 0) {
    log_error("Buffer overflow prevented");
    return -1;
}

3. Leverage Compiler and OS Protections

These don't replace input validation but add layers of defense:

Protection How to Enable What It Catches
Stack Canaries -fstack-protector-strong Stack-based overflows
ASLR OS-level (default on modern Linux) Makes exploitation harder
FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 Detects some memcpy overflows at runtime
AddressSanitizer -fsanitize=address Detects heap overflows during testing
Valgrind Run during CI Memory error detection

Add these to your build pipeline:

CFLAGS += -Wall -Wextra -fstack-protector-strong -D_FORTIFY_SOURCE=2
# For testing/CI:
CFLAGS_TEST += -fsanitize=address,undefined

4. Fuzz Network Input Parsers

This vulnerability is exactly what fuzz testing is designed to find. Tools like AFL++ or libFuzzer can automatically generate malformed packets with extreme caplen values:

# Example: fuzz the TZSP parser with AFL++
afl-fuzz -i corpus/ -o findings/ -- ./tzsp_parser_harness @@

A good fuzzing harness for a packet parser would have found this bug immediately by generating a packet with caplen = UINT32_MAX.

5. Apply the Principle of Least Privilege

Even if this vulnerability were exploited, running the TZSP forwarder as a non-root user with only the CAP_NET_RAW capability (instead of full root) would limit the blast radius:

# Grant only the needed capability, not full root
setcap cap_net_raw+ep /usr/sbin/tzsp_forwarder

6. Static Analysis

Tools that could have caught this vulnerability:

  • Coverity / CodeQL: Taint analysis tracks attacker-controlled data to dangerous sinks like memcpy
  • Clang Static Analyzer: Detects some buffer overflow patterns
  • Flawfinder / rats: Flags dangerous C functions like memcpy for manual review
# Quick scan with flawfinder
flawfinder contrib/tzsp_forwarder.c

Security Standards & References

  • CWE-120: Buffer Copy without Checking Size of Input ("Classic Buffer Overflow") — https://cwe.mitre.org/data/definitions/120.html
  • CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer
  • OWASP: Input Validation Cheat Sheet — https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html
  • SEI CERT C Coding Standard: Rule ARR38-C — Guarantee that library functions do not form invalid pointers
  • SANS CWE Top 25: Buffer overflow vulnerabilities consistently rank in the top 5 most dangerous software weaknesses

Conclusion

This vulnerability is a textbook example of why input validation at trust boundaries is non-negotiable in systems programming. A single missing bounds check on an attacker-controlled caplen field turned a routine memcpy into a potential remote code execution primitive.

The key takeaways from this fix:

Validate all external length values before using them in memory operations

Fail loudly — log and discard malformed packets rather than silently truncating

Guard against integer arithmetic issues in bounds calculations

Layer your defenses — compiler flags, ASLR, and least privilege all reduce impact even if a bug slips through

Fuzz your parsers — automated fuzzing is highly effective at finding exactly this class of bug

Buffer overflows have been a top vulnerability class for over 30 years — since the Morris Worm in 1988. They're not going away as long as C and C++ are used for network-facing code. The tools and techniques to prevent them are well-understood; the challenge is consistently applying them.

Write the bounds check. Save the heap.


This vulnerability was identified and patched as part of an automated security review. Special thanks to the OrbisAI Security scanner for flagging this critical issue.

Found a security vulnerability? Practice responsible disclosure and report it to the project maintainers before going public.

Frequently Asked Questions

What is a heap buffer overflow?

A heap buffer overflow occurs when a program writes more data to a heap-allocated buffer than it can hold, corrupting adjacent memory structures and potentially allowing attackers to execute arbitrary code or crash the application.

How do you prevent heap buffer overflow in C?

Always validate that the size parameter for memory operations like `memcpy()` does not exceed the destination buffer's allocated size. Use safe functions, implement strict bounds checking, and never trust size values from external input.

What CWE is heap buffer overflow?

Heap buffer overflow is classified as CWE-120 (Buffer Copy without Checking Size of Input) or CWE-122 (Heap-based Buffer Overflow) depending on the specific context.

Is using malloc() with the correct size enough to prevent heap buffer overflow?

No, allocating the correct buffer size is only half the solution. You must also validate that any subsequent operations (like `memcpy`) don't write beyond the allocated size, especially when the copy length comes from untrusted input.

Can static analysis detect heap buffer overflow?

Yes, static analysis tools can detect many heap buffer overflow patterns, especially when size parameters flow from untrusted sources to memory copy functions without validation. Tools like Coverity, CodeQL, and Semgrep can identify these vulnerabilities.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #3976

Related Articles

critical

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

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 libficus.c sprintf() and how to fix it

A buffer overflow vulnerability was discovered in `runtime/ficus/impl/libficus.c` where `sprintf()` was used to write a formatted compiler version string into a fixed-size stack buffer without any bounds checking. The fix replaces both vulnerable `sprintf()` calls with `snprintf()`, passing `sizeof(cver)` as the maximum write length to ensure the buffer can never be overrun. This change eliminates the risk of stack memory corruption that could be triggered by an attacker with control over the bu

critical

How buffer overflow via strcpy() happens in C Kconfig parsing and how to fix it

A critical buffer overflow vulnerability was discovered in the Linux kernel's Kconfig build system where `strcpy()` copied user-controlled symbol values into a fixed-size buffer without bounds checking. This flaw in `scripts/kconfig/symbol.c` could allow attackers to overwrite adjacent memory when processing malicious Kconfig files. The fix replaces the unsafe `strcpy()` with `memcpy()` using explicit length calculations.

high

How integer overflow in malloc happens in C bipartite matching and how to fix it

A high-severity integer overflow vulnerability was discovered in the bipartite matching algorithm implementation where unchecked multiplication operations for memory allocation could wrap around, causing undersized buffer allocations and subsequent heap overflow. The fix replaces vulnerable `malloc(sizeof(int) * V)` patterns with safe `calloc(V, sizeof(int))` calls and adds proper bounds validation to prevent exploitation.

high

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

A high-severity buffer overflow vulnerability was discovered in `profile.c` where `sprintf()` was used to format server addresses without any bounds checking. An attacker who could influence the `SERVER_BASE_PORT` value or trigger integer overflow in the port calculation could write beyond the `server_address` buffer. The fix replaces `sprintf()` with `snprintf()` using explicit buffer size limits at both call sites (lines 99 and 220).