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.

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.

Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #1102

Related Articles

critical

deleteNestedProperty Prototype Pollution via Dot-Notation Path

The `deleteNestedProperty` function in propertyUtils.ts allowed attackers to manipulate JavaScript object prototypes by passing specially crafted dot-notation paths like `__proto__.polluted`. A fix now blocks dangerous keys before processing, preventing prototype pollution attacks that could affect all objects in the application.

high

How Denial of Service via Infinite Loop Happens in JavaScript Dependencies and How to Fix It

CVE-2026-67213 is a high-severity denial of service vulnerability in nanoid before version 5.1.6 that triggers an infinite loop during random ID generation when processing specially crafted input. We upgraded nanoid across the entire dependency tree to patch this flaw and prevent attackers from freezing application threads. This fix ensures that ID generation remains resilient even when handling adversarial input patterns.

high

How Sensitive Data Exposure happens in Zotero plugins and how to fix it

A high-severity data exposure vulnerability in `Zotero.ts` automatically transmitted complete document metadata—including private notes, attachment paths, and tags—to external LLM services without user consent. The fix replaces broad `item.toJSON()` serialization with explicit field selection, sending only essential bibliographic data.

high

How missing dependency update cooldowns happen in GitHub Dependabot configurations and how to fix it

A semgrep scan flagged `.github/dependabot.yml` for lacking a cooldown period, meaning Dependabot would immediately propose updates to brand-new package versions across npm, Bundler, and Docker ecosystems. The fix adds a `cooldown: default-days: 7` block to every `package-ecosystem` entry, forcing a one-week waiting period before newly published releases are considered — reducing exposure to malicious or unstable package drops.

high

How Path Traversal Happens in TensorFlow's Data Service and How to Fix It

TensorFlow's data service dispatcher validated dataset IDs against forward-slash traversal attacks but overlooked backslash characters on non-Windows platforms, allowing attackers to escape the root directory. A targeted fix adds explicit backslash validation across all platforms, closing a high-severity path traversal vulnerability in the snapshot management system.

critical

How Unbounded WebSocket Message Handling Causes Resource Exhaustion in Node.js and How to Fix It

The WebSocketCrossServerAdapter class in a popular Node.js WebSocket library lacked any rate limiting on inbound messages, allowing attackers to flood Redis nodes and WebSocket servers with high-volume traffic. The fix introduces a configurable `rateLimit` option that caps messages per connection per second, preventing resource exhaustion while preserving legitimate functionality.