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.
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 notspecific to any convergence layer: the fault is in the shared BP acquisition
path reached after a CLA has delivered bundle bytes to
bpEndAcq(). In affectedrevisions, that path records the zero-length payload and then calls
zco_clone(..., length = 0), violating the ZCO API precondition and terminatingthe 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-neededflag are not impacted strongly.The assertion will be logged, but the CLI will remain alive.
Details
bpEndAcq()starts a ZCO reader and callsacquireBundle()for the bundle atthe front of the acquisition ZCO (
ION-DTN/bpv7/library/libbpP.c:10140throughION-DTN/bpv7/library/libbpP.c:10172).During parsing,
acqFromWork()initializesbundle->payload.lengthto-1andcalls
acquireBlock()until it reaches the payload block(
ION-DTN/bpv7/library/libbpP.c:9211throughION-DTN/bpv7/library/libbpP.c:9268).acquireBlock()decodes theblock-type-specific data byte string length with
cbor_decode_byte_string(),stores that length in
dataLength, and, for payload blocks, assigns it directlyto
bundle->payload.length:See
ION-DTN/bpv7/library/libbpP.c:8931throughION-DTN/bpv7/library/libbpP.c:8971. For a payload encoded as CBOR byte0x40,dataLengthis0. 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:9532throughION-DTN/bpv7/library/libbpP.c:9541). It then reducesthe payload ZCO to source data with an unconditional clone:
With the empty payload, that call becomes:
zco_clone()rejects zero-length clones withCHKZERO(length > 0)inION-DTN/ici/library/zco.c:2454throughION-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:9568throughION-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-lengthpayloads still use
zco_clone(). This matcheszco_create()behavior inION-DTN/ici/library/zco.c:1707throughION-DTN/ici/library/zco.c:1729, wherea 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:
Decoded structure:
The ION log contains the assertion:
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 correspondingreceiving 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.
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.