Back to Blog
critical SEVERITY5 min read

How heap buffer overflow happens in C memcpy() with untrusted PDU length and how to fix it

A critical heap buffer overflow vulnerability was discovered in the Net-SNMP agent's trap handling code where `memcpy()` copied data from a network-controlled PDU without validating that the destination buffer could hold it. An attacker could craft a malicious SNMPv1 trap with an oversized `enterprise_length` field to corrupt heap memory. The fix adds a simple bounds check against `MAX_OID_LEN` before the copy operation.

O
By Orbis AppSec
Published June 19, 2026Reviewed June 19, 2026

Answer Summary

This is a heap buffer overflow vulnerability (CWE-120) in C where `memcpy()` in `agent_trap.c` copies network-controlled data without bounds checking. The `pdu->enterprise_length` field from parsed SNMP packets was used directly to determine copy size, allowing attackers to overflow the fixed-size `t_oid` buffer. The fix adds a validation check `pdu->enterprise_length > MAX_OID_LEN` before the memcpy operation to reject oversized inputs.

Vulnerability at a Glance

cweCWE-120
fixAdd validation that enterprise_length does not exceed MAX_OID_LEN before copying
riskRemote code execution or denial of service via crafted SNMP trap packets
languageC
root causeMissing bounds check on pdu->enterprise_length before memcpy to fixed-size buffer
vulnerabilityHeap Buffer Overflow via Unchecked memcpy

Introduction

The agent_trap.c file in Net-SNMP handles SNMP trap message processing—a critical network management function that receives and processes trap PDUs (Protocol Data Units) from various sources. At line 637, a dangerous pattern lurked: the netsnmp_build_trap_oid() function used memcpy() to copy enterprise OID data without validating that the destination buffer t_oid could actually hold the incoming data.

The vulnerable code trusted the pdu->enterprise_length field directly from parsed network packets. Since SNMP trap PDUs arrive over the network from potentially untrusted sources, an attacker could craft a malicious packet with an enterprise_length value exceeding the fixed MAX_OID_LEN (typically 128) buffer capacity, triggering heap memory corruption.

The Vulnerability Explained

What Made This Code Dangerous

Here's the vulnerable code pattern at line 637:

if (pdu->trap_type == SNMP_TRAP_ENTERPRISESPECIFIC) {
    if (*t_oid_len < (pdu->enterprise_length + 2))
        return SNMPERR_LONG_OID;
    memcpy(t_oid, pdu->enterprise, pdu->enterprise_length*sizeof(oid));

The problem? The check *t_oid_len < (pdu->enterprise_length + 2) only validates against the caller-provided length parameter, not against the actual maximum capacity of the t_oid buffer. If *t_oid_len is set to a large value (or if pdu->enterprise_length is crafted to be enormous), the memcpy() will happily write past the end of t_oid.

The Attack Scenario

An attacker targeting this vulnerability would:

  1. Craft a malicious SNMPv1 trap PDU with the enterprise_length field set to a value like 256 (double MAX_OID_LEN)
  2. Send this packet to the SNMP agent's trap receiving port (typically UDP 162)
  3. Trigger the overflow when netsnmp_build_trap_oid() processes the trap

The memcpy() would then write 256 * sizeof(oid) bytes (potentially 2048 bytes on a 64-bit system) into a buffer designed for only 128 * sizeof(oid) bytes. This heap corruption could:

  • Crash the SNMP agent (denial of service)
  • Overwrite heap metadata enabling further exploitation
  • Potentially achieve remote code execution by corrupting function pointers or other critical data structures

Why This Pattern Is Especially Dangerous

The pdu->enterprise and pdu->enterprise_length fields come directly from network packet parsing. Network protocols frequently include length fields that attackers can manipulate. Any code that trusts these length values without validation against known buffer limits creates an exploitable condition.

The Fix

The One-Line Defense

The fix adds explicit validation against MAX_OID_LEN before the dangerous memcpy():

Before (vulnerable):

if (pdu->trap_type == SNMP_TRAP_ENTERPRISESPECIFIC) {
    if (*t_oid_len < (pdu->enterprise_length + 2))
        return SNMPERR_LONG_OID;
    memcpy(t_oid, pdu->enterprise, pdu->enterprise_length*sizeof(oid));

After (fixed):

if (pdu->trap_type == SNMP_TRAP_ENTERPRISESPECIFIC) {
    if (pdu->enterprise_length > MAX_OID_LEN ||
        *t_oid_len < (pdu->enterprise_length + 2))
        return SNMPERR_LONG_OID;
    memcpy(t_oid, pdu->enterprise, pdu->enterprise_length*sizeof(oid));

The new condition pdu->enterprise_length > MAX_OID_LEN ensures that regardless of what value an attacker puts in the PDU, the copy operation will never exceed the buffer's actual capacity.

Additional Hardening: sprintf → snprintf

The PR also fixed a related issue at line 457:

Before:

sprintf(buf, ":%hu", sinkport);

After:

snprintf(buf, sizeof(buf), ":%hu", sinkport);

While this particular sprintf() wasn't directly exploitable (the port number format is bounded), replacing it with snprintf() follows defense-in-depth principles and prevents any future issues if the format string changes.

Regression Test Coverage

The fix includes a comprehensive unit test (T107build_trap_oid_cagentlib.c) that specifically validates the bounds check:

/* enterprise_length > MAX_OID_LEN must be rejected */
pdu = snmp_pdu_create(SNMP_MSG_TRAP);
pdu->trap_type = SNMP_TRAP_ENTERPRISESPECIFIC;
pdu->enterprise_length = MAX_OID_LEN + 1;
pdu->enterprise = calloc(pdu->enterprise_length, sizeof(oid));
t_oid_len = sizeof(t_oid) / sizeof(t_oid[0]);
rc = netsnmp_build_trap_oid(pdu, t_oid, &t_oid_len);
OKF(rc == SNMPERR_LONG_OID,
    ("enterprise_length %zu > MAX_OID_LEN should return SNMPERR_LONG_OID"));

This test ensures that any future code changes that accidentally remove the bounds check will be caught immediately.

Prevention & Best Practices

Rules for Safe Memory Operations in C

  1. Never trust length fields from network input — Always validate against known buffer capacities, not just caller-provided parameters

  2. Use explicit maximum constants — Define constants like MAX_OID_LEN and check against them before any copy operation

  3. Prefer bounded functions — Use snprintf() over sprintf(), strncpy() over strcpy(), and always pass explicit size limits

  4. Validate early, copy late — Check all bounds conditions before performing the actual memory operation

Code Review Checklist for memcpy()

When reviewing C code with memcpy(), ask:
- ✅ Is the source length validated against the destination buffer size?
- ✅ Does the length come from untrusted input (network, file, user)?
- ✅ Is there an explicit check against a maximum constant?
- ✅ What happens if the length is 0? Negative? Maximum size_t?

Similar Patterns to Watch

The PR notes that similar patterns exist at other locations in the same file:
- agent/agent_trap.c:610
- agent/agent_trap.c:638
- agent/agent_trap.c:646
- agent/agent_trap.c:1646
- agent/agent_trap.c:1665

Any code review should examine all similar patterns, not just the one flagged by automated tools.

Key Takeaways

  • Network-controlled length fields in SNMP PDUs must be validated against MAX_OID_LEN before any buffer copy — the enterprise_length field was trusted without bounds checking
  • The netsnmp_build_trap_oid() function now rejects any enterprise_length exceeding MAX_OID_LEN with SNMPERR_LONG_OID
  • A single condition check (pdu->enterprise_length > MAX_OID_LEN) completely prevents this class of attack — simple fixes can have enormous security impact
  • Unit tests that specifically probe boundary conditions (like MAX_OID_LEN + 1) provide lasting protection against regressions
  • When one vulnerable pattern is found, search for similar patterns — the PR identified 5 additional locations needing review

How Orbis AppSec Detected This

  • Source: The pdu->enterprise_length field parsed from incoming SNMP trap PDUs received over the network
  • Sink: memcpy(t_oid, pdu->enterprise, pdu->enterprise_length*sizeof(oid)) in agent/agent_trap.c:637
  • Missing control: No validation that enterprise_length does not exceed MAX_OID_LEN (the actual buffer capacity)
  • CWE: CWE-120 (Buffer Copy without Checking Size of Input)
  • Fix: Added bounds check pdu->enterprise_length > MAX_OID_LEN before the memcpy operation

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

This heap buffer overflow in agent_trap.c demonstrates a classic C vulnerability pattern: trusting a length field from network input without validating it against the actual buffer capacity. The fix—a single additional condition checking pdu->enterprise_length > MAX_OID_LEN—completely eliminates the attack surface.

For developers working with network protocols in C, this case reinforces a critical lesson: every length field from untrusted input is a potential attack vector. Explicit bounds checking against known maximum values isn't just good practice—it's the difference between secure code and exploitable code.

References

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 and potentially allowing attackers to execute arbitrary code or crash the application.

How do you prevent buffer overflows in C?

Always validate that the source data size does not exceed the destination buffer capacity before using memcpy(), strcpy(), or similar functions. Use bounded alternatives like snprintf() and explicitly check length fields from untrusted input.

What CWE is heap buffer overflow?

CWE-120 (Buffer Copy without Checking Size of Input) covers this vulnerability class where data is copied to a buffer without verifying the input fits within the buffer bounds.

Is using a large buffer enough to prevent buffer overflows?

No, attackers can often control the length field to exceed any fixed buffer size. Explicit bounds checking against the actual buffer capacity is required regardless of buffer size.

Can static analysis detect buffer overflows?

Yes, static analysis tools can detect many buffer overflow patterns by tracking data flow from untrusted sources to memory operations and flagging missing bounds checks.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #1102

Related Articles

high

How missing Dependabot cooldown happens in GitHub Actions and how to fix it

A high-severity configuration vulnerability was discovered in a `.github/dependabot.yml` file that lacked a cooldown period for package updates. Without this safeguard, Dependabot could immediately propose updates to newly published package versions—including potentially malicious or unstable releases. The fix adds a simple `cooldown` block with a 7-day waiting period before any new package version is suggested.

high

How Server-Sent Events Injection via Unsanitized Newlines happens in Node.js h3 and how to fix it

A high-severity Server-Sent Events (SSE) injection vulnerability (CVE-2026-33128) was discovered in the h3 HTTP framework, where unsanitized newline characters in event stream fields could allow attackers to inject arbitrary SSE messages. The fix upgrades h3 from version 1.15.5 to 1.15.6 in the frontend's dependency tree, ensuring that newline characters are properly sanitized before being written to event streams.

high

How Memory Exhaustion via Large Comma-Separated Selector Lists happens in Python Soup Sieve and how to fix it

A high-severity memory exhaustion vulnerability (CVE-2026-49476) was discovered in Soup Sieve version 2.8.3, affecting Python applications that parse CSS selectors from user-controlled input. The vulnerability allows attackers to craft malicious selector lists that consume excessive memory, potentially causing denial of service. The fix involves upgrading to soupsieve 2.8.4, which implements proper resource limits on selector parsing.

high

How prototype pollution via `__proto__` key happens in Node.js defu and how to fix it

A high-severity prototype pollution vulnerability (CVE-2026-35209) was discovered in the `defu` package version 6.1.4, which allowed attackers to inject properties into JavaScript's `Object.prototype` via the `__proto__` key in defaults arguments. The fix upgrades `defu` to version 6.1.5 in the frontend's dependency tree, protecting downstream consumers like `c12` and `dotenv` configuration loaders from malicious property injection.

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.

high

How memory exhaustion via large comma-separated selector lists happens in Python soupsieve and how to fix it

A high-severity memory exhaustion vulnerability (CVE-2026-49476) was discovered in soupsieve 2.8.3, a CSS selector library used by BeautifulSoup in Python. An attacker who could influence CSS selector input could craft large comma-separated selector lists to exhaust system memory, causing denial of service. The fix upgrades soupsieve from 2.8.3 to 2.8.4 in the backend's `uv.lock` dependency file.