Skip to content

Remote BPv7 bundle acquisition denial of service via zero-length payload assertion

High
iondev33 published GHSA-27wg-h3xq-4p9g Jul 2, 2026

Package

bpv7 (ION)

Affected versions

> 3.7.1, < 4.2.0-b (builds without --disable-core-file-needed)

Patched versions

4.2.0-b

Description

Summary

An unauthenticated sender can crash ION BPv7 bundle acquisition by sending a
single syntactically valid BPv7 bundle whose payload block is present but whose
payload data is the empty definite-length CBOR byte string h'' (0x40). This is not
specific to any convergence layer: the fault is in the shared BP acquisition
path reached after a CLA has delivered bundle bytes to bpEndAcq(). In affected
revisions, that path records the zero-length payload and then calls
zco_clone(..., length = 0), violating the ZCO API precondition and terminating
the receiving process with an assertion failure. The impact is denial of service
for any exposed or otherwise attacker-reachable CLA/input path that accepts BPv7
bundle bytes into the common acquisition API.

Builds of ION which use the --disable-core-file-needed flag are not impacted strongly.
The assertion will be logged, but the CLI will remain alive.

Details

bpEndAcq() starts a ZCO reader and calls acquireBundle() for the bundle at
the front of the acquisition ZCO (ION-DTN/bpv7/library/libbpP.c:10140 through
ION-DTN/bpv7/library/libbpP.c:10172).

During parsing, acqFromWork() initializes bundle->payload.length to -1 and
calls acquireBlock() until it reaches the payload block
(ION-DTN/bpv7/library/libbpP.c:9211 through
ION-DTN/bpv7/library/libbpP.c:9268). acquireBlock() decodes the
block-type-specific data byte string length with cbor_decode_byte_string(),
stores that length in dataLength, and, for payload blocks, assigns it directly
to bundle->payload.length:

dataLength = uvtemp;
...
if (blkType == PayloadBlk)
{
        bundle->payloadBlockProcFlags = blkProcFlags;
        bundle->payload.length = dataLength;
        bundle->payload.crcType = crcType;
        bytesParsed = bytesToParse - unparsedBytes;
        work->preambleLength += bytesParsed;
        return bytesParsed;
}

See ION-DTN/bpv7/library/libbpP.c:8931 through
ION-DTN/bpv7/library/libbpP.c:8971. For a payload encoded as CBOR byte
0x40, dataLength is 0. This is not the same as omitting the payload block:
BPv7 requires exactly one payload block, and payload block data is a
definite-length CBOR byte string. The specification does not impose a minimum
nonzero payload byte string length for this case.

After parsing, acquireBundle() splits the raw bundle out of the work ZCO
(ION-DTN/bpv7/library/libbpP.c:9532 through
ION-DTN/bpv7/library/libbpP.c:9541). It then reduces
the payload ZCO to source data with an unconditional clone:

bundle->payload.content = zco_clone(sdr, work->rawBundle,
                work->preambleLength, bundle->payload.length);

With the empty payload, that call becomes:

zco_clone(sdr, work->rawBundle, work->preambleLength, 0)

zco_clone() rejects zero-length clones with CHKZERO(length > 0) in
ION-DTN/ici/library/zco.c:2454 through ION-DTN/ici/library/zco.c:2464,
causing the receiving process to assert before normal bundle processing can
complete.

The current checkout contains the expected guard in
ION-DTN/bpv7/library/libbpP.c:9568 through ION-DTN/bpv7/library/libbpP.c:9577:
zero-length payloads are represented with an empty inbound ZCO via
zco_create(sdr, ZcoSdrSource, 0, 0, 0, ZcoInbound), while positive-length
payloads still use zco_clone(). This matches zco_create() behavior in
ION-DTN/ici/library/zco.c:1707 through ION-DTN/ici/library/zco.c:1729, where
a ZCO with no first extent and length zero is valid.

PoC

The following bundle can be sent as a datagram to a UDPCLI to reproduce this bug:

9f 89 07 00 01 82 02 82 02 01 82 02 82 02 01 82 02 82 02 01
82 01 00 1b 00 00 02 de c1 f4 2c 00 42 14 d5 86 01 01 00 01
40 42 61 78 ff

Decoded structure:

top-level bundle array: indefinite-length array
primary block: version 7, no bundle flags, CRC16/X-25 value 0x14d5
destination/source/report-to: ipn:2.1
creation timestamp: [1, 0]
lifetime: 3155760000000 ms
payload block: type 1, block number 1, no block flags
payload block data: 0x40, the empty definite-length CBOR byte string
payload block CRC16/X-25 value: 0x6178

The ION log contains the assertion:

at line 2464 of ../library/zco.c, Assertion failed. (length > 0)

Impact

This is a remotely triggerable denial-of-service vulnerability in the shared BPv7
bundle acquisition path. UDP is the demonstrated PoC transport, but the crash is
not UDP-specific. Any deployment that exposes a CLA or other input adapter that
feeds untrusted BPv7 bundle bytes into bpEndAcq() can have the corresponding
receiving process killed by a small, valid bundle. This can interrupt bundle
reception for the affected induct and may require supervisor restart or operator
action, depending on deployment configuration.

Patches

Fixed in ION 4.2.0-b (nasa-jpl/ION-DTN). acquireBundle() now represents a zero-length BPv7 payload with an empty inbound ZCO via zco_create() instead of calling zco_clone() with length 0, which tripped the CHKZERO(length > 0) assertion in ici/library/zco.c.

  • Fix commit: <public ION-DTN commit hash once the fix is merged/mirrored>
  • Development PR: nasa-jpl/ion-ios-dev#1148

Workarounds

Configure and build ION with --disable-core-file-needed: the assertion is logged but the process is not terminated, downgrading the impact from a crash to a logged, non-fatal event. This is a mitigation, not a fix — upgrade to 4.2.0-b or later.

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
None
Availability
High

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

CVE ID

No known CVE

Weaknesses

Reachable Assertion

The product contains an assert() or similar statement that can be triggered by an attacker, which leads to an application exit or other behavior that is more severe than necessary. Learn more on MITRE.

Credits