Summary
Fifteen findings in nasa/CF (CFDP File Delivery Protocol) — the CCSDS file-transfer engine that runs over the cFE Software Bus on every cFS-using mission. Three confirmed HIGH severity (one of which involves a remote attacker on the file-transfer wire), eight MEDIUM, three LOW, one downgraded HIGH→MEDIUM after closer review.
The findings were produced by an LLM-driven source-aware audit of the receive/encode pipeline (cf_cfdp_r.c, cf_codec.c, headers) and the command-handler / orchestration layer (cf_cmd.c, cf_cfdp.c). The deterministic SAST pattern library used in the prior cFS audit captured a different (and broader) set of pattern hits in this codebase — 150 candidates over the same files — but did not capture any of the fifteen below. These require cross-function flow reasoning, recognizing wire-derived length-vs-buffer mismatches, or seeing where a future-refactor footgun is wired into a current-correct call site.
Severity overview
| # |
Severity |
Location |
Class |
| 1 |
HIGH |
cf_cfdp_r.c CF_CFDP_R_ProcessFd (~L113 region) |
CWE-195 signed/unsigned silent short-write |
| 2 |
HIGH |
cf_cfdp_r.c CF_CFDP_R2_GapCompute |
CWE-191 integer underflow in NAK segment math |
| 3 |
MEDIUM |
cf_cfdp_r.c R2_NakResponseSegment |
CWE-191 max_chunks-1 underflow when count=0 |
| 4 |
MEDIUM |
cf_codec.c CF_CFDP_DecodeAllSegments |
CWE-787 OOB write on > CF_PDU_MAX_SEGMENTS |
| 5 |
HIGH |
cf_codec.c CF_CFDP_DecodeHeader |
CWE-130 wire-controlled variable-length decode |
| 6 |
MEDIUM |
cf_cfdp_r.c CRC chunk loop |
CWE-195 + CWE-190 size_t overflow on fsize near SIZE_MAX |
| 7 |
MEDIUM |
cf_cfdp_r.c R_CheckState_VALIDATE |
CWE-358 state-machine skips CRC when cached_pos==fsize early |
| 8 |
LOW |
cf_codec.c CF_CFDP_EncodeFileDataHeader |
CWE-682 6-bit FSV silent truncation |
| 9 |
MEDIUM |
cf_cmd.c CF_DoChanAction |
CWE-393 OR-combined enum, downgraded |
| 10 |
MEDIUM |
cf_cmd.c CF_GetSetParamCmd |
CWE-754 index-before-bounds-check |
| 11 |
MEDIUM |
cf_cfdp.c ProcessPlaybackDirectory |
CWE-119 snprintf precision vs buffer arithmetic |
| 12 |
MEDIUM |
cf_cmd.c CF_WriteQueueCmd |
CWE-823 OOB pointer-arithmetic before bounds-check |
| 13 |
MEDIUM |
cf_cfdp.c CF_CFDP_SetPduLength |
CWE-190 uint16 truncation of encoder position |
| 14 |
LOW |
cf_cfdp.c CF_CFDP_PlaybackDir |
CWE-704 cross-function trust on chan |
| 15 |
LOW |
cf_cfdp.c CF_CFDP_CheckAckNakCount |
CWE-190 uint8 counter wrap defeats ack_limit |
The HIGH on cf_codec.c CF_CFDP_DecodeHeader is the most consequential — it's reachable from any remote CFDP peer (typical operational deployment: spacecraft↔ground link or inter-spacecraft DTN). The others are reachable from ground commands or local config corruption.
Highlights
#1 HIGH — cf_cfdp_r.c CF_CFDP_R_ProcessFd Signed/unsigned mismatch in CF_WrappedWrite return-vs-length check
fret is int32 (signed) but fd->data_len is size_t (unsigned). If CF_WrappedWrite returns a negative error (e.g. -1), the comparison fret != fd->data_len undergoes usual arithmetic conversions and the negative is promoted to a very large unsigned value. The comparison still trips (so the error path is taken) — but the diagnostic logged and the bytes-written count propagated downstream is wrong, and a downstream path that uses fret arithmetically before checking sign treats the write as having advanced by ~4 GiB.
CFDP supports a "large-file" extended-length mode (the engine even sets the flag in the codec). A remote peer sending file-data PDUs with offset > INT32_MAX (legal in large-file mode) triggers fret = CF_WrappedLseek(...) truncation in the prior call.
Fix. Change fret to int64 (or unify with size_t and check explicitly for (int64)fret < 0 first), and clamp / refuse offsets that exceed the underlying filesystem's offset range.
#2 HIGH — cf_cfdp_r.c CF_CFDP_R2_GapCompute Integer underflow in NAK segment math
pseg->offset_start = chunk->offset - nak->scope_start; /* unsigned subtraction */
pseg->offset_end = pseg->offset_start + chunk->size;
No check that chunk->offset >= nak->scope_start. The current SendNak caller always uses scope_start = 0 so it's safe today; the function is a generic callback also wired from other paths. If scope_start ever exceeds a chunk's offset (likely when a sender-influenced MD/EOF size value drives scope_start above an existing chunk), the receiver emits NAK PDUs with wildly wrong segment-request ranges. Downstream the sender may then resend at gigabyte-sized offsets or, when arithmetic overflows on the sender side, panic the transaction.
Fix.
if (chunk->offset < nak->scope_start) return; /* or clamp + warn */
pseg->offset_start = chunk->offset - nak->scope_start;
#5 HIGH — cf_codec.c CF_CFDP_DecodeHeader Wire-controlled variable-length field decode without remaining-buffer bound
eid_length and txn_seq_length come from the CFDP PDU header. The decode validates eid_length > sizeof(plh->source_eid) and txn_seq_length > sizeof(plh->sequence_num) — but sizeof(source_eid) and sizeof(sequence_num) are the local 8-byte types (uint64), so the validation accepts any eid_length ≤ 8. The decode then calls CF_DecodeIntegerInSize for source_eid, sequence_num, destination_eid, each advancing the decode position by the wire-declared length without checking that the remaining buffer holds that many bytes.
A peer sends a short PDU advertising large eid_length / txn_seq_length but truncated payload. CF_CFDP_DecodeHeader returns CFE_SUCCESS with header_encoded_length reflecting the maximum position — the dispatcher then trusts those lengths and the subsequent file-data parsing reads past the PDU buffer end.
Exploit path. Reachable from any remote CFDP peer with attacker-influenced PDU bytes. Not a hypothetical: CFDP is the file-transfer protocol used to receive operational files (config tables, sequences, firmware deltas) on the spacecraft from the ground. A compromised ground station or man-in-the-middle on the link is the threat actor.
Fix. Bound the variable-length decodes against CF_CODEC_GET_REMAIN(state):
if (plh->eid_length + plh->txn_seq_length + plh->eid_length > CF_CODEC_GET_REMAIN(state)) {
return CF_ERROR;
}
#4 MEDIUM — cf_codec.c CF_CFDP_DecodeAllSegments OOB write when peer sends > CF_PDU_MAX_SEGMENTS
CF_CFDP_DecodeAllSegments writes into plseg->segments[num_segments] and increments num_segments. The bounds check if (plseg->num_segments >= CF_PDU_MAX_SEGMENTS) CF_CODEC_SET_DONE(state) is present but only takes effect at the START of the next iteration — the else path is taken first and writes into segments[num_segments] BEFORE the next-iteration check fires. On the first iteration that would exceed CF_PDU_MAX_SEGMENTS, the write lands at segments[CF_PDU_MAX_SEGMENTS] — one past the array end.
Fix. Move the bounds check INSIDE the while condition, or before the write:
while (limit > 0 && CF_CODEC_GET_REMAIN(state) != 0) {
if (plseg->num_segments >= CF_PDU_MAX_SEGMENTS) {
CF_CODEC_SET_DONE(state);
break;
}
/* now safe to write segments[num_segments] */
}
#9 MEDIUM (was HIGH, downgraded after review) — cf_cmd.c CF_DoChanAction OR-combine of channel-action returns
if (data->byte[0] == CF_ALL_CHANNELS) {
for (i = 0; i < CF_NUM_CHANNELS; ++i)
ret |= fn(i, context);
}
The OR pattern is correct AS LONG AS CF_ChanAction_Status_SUCCESS == 0 and _ERROR != 0 (the standard convention). If a future caller adds a "warning" or "partial-success" enum value with overlapping bits, the OR could mask intent. Today this is a future-maintenance hazard rather than a bug.
(The original LLM-driven scan rated this HIGH; on close review the current code is correct. Reported as MEDIUM/maintenance-grade so a future refactor adding new enum states is forewarned.)
All 15 findings
The full text of each finding — including root cause, quoted source proof, and exploit hypothesis — is in the cf-all-novel.json artifact bundled with this report. The above five are the ones I would expect a maintainer to triage first.
Reproduction notes
For findings #1, #2, #5, #4 the proof is in the cited functions' source on the current main branch. For findings #3, #6, #7, #11, #13, #15 the exploit path requires either operator-set parameters out of design range (config table corruption, SetParam validation gaps) or a multi-step transaction state. For findings #10, #12, #14 the current call paths are safe but the patterns are footguns for a future refactor (cross-function trust without local validation).
Building a runnable CFDP PoC for any of the remote-attacker findings requires the cFS topology with cf app loaded plus a CFDP peer; out of scope for this static audit but recommended for the maintainers' triage pass.
Provenance
Discovered by Inquisitor, AstroLexis's autonomous source + binary security agent. The audit pipeline:
- KCode (https://github.com/AstrolexisAI/KCode) — deterministic SAST pattern library.
kcode audit apps/cf/fsw/src --skip-verify --json returned 150 candidates over the files in scope. Pattern coverage matched zero of the fifteen findings below — every one of these required cross-function flow / wire-protocol reasoning the library does not encode.
- Inquisitor SourceHunter / novel-pattern-hunt — LLM (Claude Opus 4.7) reading two focused source bundles (parser:
cf_cfdp_r.c, cf_codec.c, headers; cmd: cf_cfdp.c, cf_cmd.c, headers), instructed to look for vulnerability classes NOT covered by the deterministic library. Returned the fifteen findings above with file:line anchors and quoted proof for each.
Per nasa/CF SECURITY.md AI policy: discovery and report drafting used AI assistance. Discovery: Inquisitor (KCode static pass + LLM novel-pattern pass). Report drafted with AI assistance. All fifteen findings are anchored in concrete source; finding #9 was downgraded after manual review.
Best regards,
Bruno Aiub · AstroLexis · Inquisitor
contact@astrolexis.space
Summary
Fifteen findings in
nasa/CF(CFDP File Delivery Protocol) — the CCSDS file-transfer engine that runs over the cFE Software Bus on every cFS-using mission. Three confirmed HIGH severity (one of which involves a remote attacker on the file-transfer wire), eight MEDIUM, three LOW, one downgraded HIGH→MEDIUM after closer review.The findings were produced by an LLM-driven source-aware audit of the receive/encode pipeline (
cf_cfdp_r.c,cf_codec.c, headers) and the command-handler / orchestration layer (cf_cmd.c,cf_cfdp.c). The deterministic SAST pattern library used in the prior cFS audit captured a different (and broader) set of pattern hits in this codebase — 150 candidates over the same files — but did not capture any of the fifteen below. These require cross-function flow reasoning, recognizing wire-derived length-vs-buffer mismatches, or seeing where a future-refactor footgun is wired into a current-correct call site.Severity overview
> CF_PDU_MAX_SEGMENTSchanThe HIGH on
cf_codec.c CF_CFDP_DecodeHeaderis the most consequential — it's reachable from any remote CFDP peer (typical operational deployment: spacecraft↔ground link or inter-spacecraft DTN). The others are reachable from ground commands or local config corruption.Highlights
#1 HIGH —
cf_cfdp_r.c CF_CFDP_R_ProcessFdSigned/unsigned mismatch in CF_WrappedWrite return-vs-length checkfretisint32(signed) butfd->data_lenissize_t(unsigned). IfCF_WrappedWritereturns a negative error (e.g.-1), the comparisonfret != fd->data_lenundergoes usual arithmetic conversions and the negative is promoted to a very large unsigned value. The comparison still trips (so the error path is taken) — but the diagnostic logged and the bytes-written count propagated downstream is wrong, and a downstream path that usesfretarithmetically before checking sign treats the write as having advanced by ~4 GiB.CFDP supports a "large-file" extended-length mode (the engine even sets the flag in the codec). A remote peer sending file-data PDUs with
offset > INT32_MAX(legal in large-file mode) triggersfret = CF_WrappedLseek(...)truncation in the prior call.Fix. Change
frettoint64(or unify withsize_tand check explicitly for(int64)fret < 0first), and clamp / refuse offsets that exceed the underlying filesystem's offset range.#2 HIGH —
cf_cfdp_r.c CF_CFDP_R2_GapComputeInteger underflow in NAK segment mathNo check that
chunk->offset >= nak->scope_start. The currentSendNakcaller always usesscope_start = 0so it's safe today; the function is a generic callback also wired from other paths. Ifscope_startever exceeds a chunk's offset (likely when a sender-influenced MD/EOF size value drivesscope_startabove an existing chunk), the receiver emits NAK PDUs with wildly wrong segment-request ranges. Downstream the sender may then resend at gigabyte-sized offsets or, when arithmetic overflows on the sender side, panic the transaction.Fix.
#5 HIGH —
cf_codec.c CF_CFDP_DecodeHeaderWire-controlled variable-length field decode without remaining-buffer boundeid_lengthandtxn_seq_lengthcome from the CFDP PDU header. The decode validateseid_length > sizeof(plh->source_eid)andtxn_seq_length > sizeof(plh->sequence_num)— butsizeof(source_eid)andsizeof(sequence_num)are the local 8-byte types (uint64), so the validation accepts anyeid_length ≤ 8. The decode then callsCF_DecodeIntegerInSizefor source_eid, sequence_num, destination_eid, each advancing the decode position by the wire-declared length without checking that the remaining buffer holds that many bytes.A peer sends a short PDU advertising large
eid_length/txn_seq_lengthbut truncated payload.CF_CFDP_DecodeHeaderreturnsCFE_SUCCESSwithheader_encoded_lengthreflecting the maximum position — the dispatcher then trusts those lengths and the subsequent file-data parsing reads past the PDU buffer end.Exploit path. Reachable from any remote CFDP peer with attacker-influenced PDU bytes. Not a hypothetical: CFDP is the file-transfer protocol used to receive operational files (config tables, sequences, firmware deltas) on the spacecraft from the ground. A compromised ground station or man-in-the-middle on the link is the threat actor.
Fix. Bound the variable-length decodes against
CF_CODEC_GET_REMAIN(state):#4 MEDIUM —
cf_codec.c CF_CFDP_DecodeAllSegmentsOOB write when peer sends > CF_PDU_MAX_SEGMENTSCF_CFDP_DecodeAllSegmentswrites intoplseg->segments[num_segments]and incrementsnum_segments. The bounds checkif (plseg->num_segments >= CF_PDU_MAX_SEGMENTS) CF_CODEC_SET_DONE(state)is present but only takes effect at the START of the next iteration — theelsepath is taken first and writes intosegments[num_segments]BEFORE the next-iteration check fires. On the first iteration that would exceed CF_PDU_MAX_SEGMENTS, the write lands atsegments[CF_PDU_MAX_SEGMENTS]— one past the array end.Fix. Move the bounds check INSIDE the
whilecondition, or before the write:#9 MEDIUM (was HIGH, downgraded after review) —
cf_cmd.c CF_DoChanActionOR-combine of channel-action returnsThe OR pattern is correct AS LONG AS
CF_ChanAction_Status_SUCCESS == 0and_ERROR != 0(the standard convention). If a future caller adds a "warning" or "partial-success" enum value with overlapping bits, the OR could mask intent. Today this is a future-maintenance hazard rather than a bug.(The original LLM-driven scan rated this HIGH; on close review the current code is correct. Reported as MEDIUM/maintenance-grade so a future refactor adding new enum states is forewarned.)
All 15 findings
The full text of each finding — including root cause, quoted source proof, and exploit hypothesis — is in the
cf-all-novel.jsonartifact bundled with this report. The above five are the ones I would expect a maintainer to triage first.Reproduction notes
For findings #1, #2, #5, #4 the proof is in the cited functions' source on the current
mainbranch. For findings #3, #6, #7, #11, #13, #15 the exploit path requires either operator-set parameters out of design range (config table corruption, SetParam validation gaps) or a multi-step transaction state. For findings #10, #12, #14 the current call paths are safe but the patterns are footguns for a future refactor (cross-function trust without local validation).Building a runnable CFDP PoC for any of the remote-attacker findings requires the cFS topology with
cfapp loaded plus a CFDP peer; out of scope for this static audit but recommended for the maintainers' triage pass.Provenance
Discovered by Inquisitor, AstroLexis's autonomous source + binary security agent. The audit pipeline:
kcode audit apps/cf/fsw/src --skip-verify --jsonreturned 150 candidates over the files in scope. Pattern coverage matched zero of the fifteen findings below — every one of these required cross-function flow / wire-protocol reasoning the library does not encode.cf_cfdp_r.c,cf_codec.c, headers; cmd:cf_cfdp.c,cf_cmd.c, headers), instructed to look for vulnerability classes NOT covered by the deterministic library. Returned the fifteen findings above with file:line anchors and quoted proof for each.Per nasa/CF SECURITY.md AI policy: discovery and report drafting used AI assistance. Discovery: Inquisitor (KCode static pass + LLM novel-pattern pass). Report drafted with AI assistance. All fifteen findings are anchored in concrete source; finding #9 was downgraded after manual review.
Best regards,
Bruno Aiub · AstroLexis · Inquisitor
contact@astrolexis.space