Skip to content

Fix #520, bound received FD PDU against declared file size - #526

Draft
0xiviel wants to merge 1 commit into
nasa:devfrom
0xiviel:fix-520-bound-fd-offset
Draft

0xiviel wants to merge 1 commit into
nasa:devfrom
0xiviel:fix-520-bound-fd-offset

Conversation

@0xiviel

@0xiviel 0xiviel commented Sep 28, 2026

Copy link
Copy Markdown

Checklist (Please check before submitting)

Describe the contribution

Fixes #520. CF_CFDP_R_ProcessFd() seeked to and wrote at a received File Data PDU's offset without comparing that offset, or offset plus data length, against txn->fsize, the size the Metadata PDU declares. A transfer declaring 4 bytes retained a 2,130,706,433-byte file and reported success. The file is sparse where the filesystem supports it: 2.0G apparent, 8192 bytes on disk on tmpfs, so the impact is a wrong file length and a false success report, not the 4 GiB of storage exhaustion #520 claims. Bounding the offset closes the whole defect, so Fixes is deliberate.

The fix has two parts, because a peer controls PDU order. CF_CFDP_R_ProcessFd() now rejects an FD PDU whose offset plus data length exceeds txn->fsize, once the MD PDU has been received. CF_CFDP_R_SubstateRecvMd() applies the same bound the moment the size becomes known, against a saturating high-water mark of what was written, the new txn->state_data.recv_top; without it, sending that FD PDU first bypasses the bound.

Testing performed

Four orderings, each with a benign 4-byte control over the same path, injected over ci_lab on UDP 1234 and decoded by the real CF_CFDP_ReceiveMessage path. Run twice at each word size, against base a9c36d6 and against this change.

ordering unpatched a9c36d6 with this change
A, MD first, crafted FD at 0x7F000000 2130706433 bytes retained, files_recv +1, file_size_mismatch +0, at both word sizes not delivered, file_size_mismatch +1, EID 167, at both word sizes
B, the same FD sent before the MD same as A same as A, rejected when the MD PDU arrives
C, FD only, no MD ever sent not delivered, temp file 2130706433 apparent / 4096 on disk unchanged
D, MD first, crafted FD at 0xFFFFFF00 4294967041 bytes retained at 64-bit; at 32-bit it already failed, but at the seek, failed to seek offset -256, on file_seek not delivered, file_size_mismatch +1, EID 167, and at 32-bit the offset is reported correctly
benign control delivered, 4 bytes delivered, 4 bytes

Case B, before and after, same input:

EVS Port1 1980-012-14:04:30.50001 66/1/CF 84: CF R2(23:342): successfully retained file as /cf/c342.dat

EVS Port1 1980-012-14:11:09.50032 66/1/CF 167: CF R2(23:342): file data past end of file, wrote 2130706433 size 4
EVS Port1 1980-012-14:11:09.59986 66/1/CF 85: CF R2(23:342): removed temp file /cf/tmp/23_342.tmp, status=0, txn_stat=6

One limit:

  • The new check bounds nothing before the MD PDU arrives, and nothing inside a size the MD PDU declares, since it is relative to that size: either way a peer holds a large sparse temporary file until the existing 30 s inactivity timer removes it, identically before and after this change. Bounding that needs a receive size limit CF does not have.

ctest is 14 of 14 with no new warnings, apps/cf/fsw/src has zero missed lines and zero missed branches, and cf_cfdp_r.c has 100 percent condition coverage.

Expected behavior changes

  • API Change: none.
  • Behavior Change: file data past the declared size is rejected whether it arrives before or after the MD PDU; the transaction fails on the existing CF_TxnStatus_FILE_SIZE_ERROR path, file_size_mismatch increments and the file is not retained. Transfers within the declared size are unaffected.
  • One new event ID, CF_CFDP_R_FD_SIZE_ERR_EID (167), and one new internal field, recv_top in CF_StateData_t, in no telemetry or table structure. file_size_mismatch has had no writer in fsw/ since a0914c4, so a quiet counter starts moving.

System(s) tested on

  • Hardware: x86-64 PC
  • OS: Linux 7.1.5 (Debian/Kali), gcc 16.2.0
  • Versions: nasa/CF at a9c36d6 on dev, cFE, OSAL and PSP from the cFS bundle at d195847. Both word sizes with ASan and UBSan, 32-bit (-m32) again with no sanitizer.

Additional context

I will post that correction on #520. The cFS-Apps Individual CLA was emailed separately, so please hold the merge until you can see it.

Third party code

None.

Contributor Info - All information REQUIRED for consideration of pull request

Eva Crystal (0xiviel), XSource Security

CF_CFDP_R_ProcessFd() seeked to and wrote at the offset carried in a
received File Data PDU without ever comparing that offset, or offset plus
data length, against txn->fsize, the size the Metadata PDU declares.
Nothing downstream notices: CF_CFDP_R_CalcCrcStart() only compares the
MD-declared size against the EOF-declared size, and
CF_CFDP_R_CheckComplete() and CF_CFDP_R_CalcCrcChunk() only look at the
first txn->fsize bytes. A receiver retained a 2130706433-byte file for a
transfer declared as 4 bytes and reported it successful.

A peer controls PDU order, so the bound is applied in two places:

* CF_CFDP_R_ProcessFd() rejects an FD PDU whose offset plus data length
  exceeds txn->fsize, once the MD PDU has been received. Before that the
  size is unknown, and file data legitimately arrives before metadata,
  which keeps class 1 and out-of-order class 2 reception working. It is
  written as a subtraction rather than a sum so offset plus length cannot
  wrap where CF_FileSize_t and size_t are the same width.
* CF_CFDP_R_SubstateRecvMd() applies the same bound the moment the size
  becomes known, comparing the highest end-of-data offset already written
  against the declared txn->fsize. Without it, sending that File Data PDU
  before the Metadata PDU bypasses the bound entirely. The mark is kept in
  txn->state_data.recv_top, not derived from the chunk list, because
  CF_ChunkListAdd() drops entries once the list is full, and it saturates
  rather than wrapping.

Both sites use the existing CF_TxnStatus_FILE_SIZE_ERROR fault path and
file_size_mismatch counter, so the state machine tears the transaction
down and removes the temporary file. One new event ID is added,
CF_CFDP_R_FD_SIZE_ERR_EID (167).

CF has no receive size limit, so file data that arrives before the
Metadata PDU, or inside a large size the Metadata PDU declares, still
produces a sparse temporary file until the channel inactivity timer
expires. That is unchanged by this commit and is not addressed here.
@dzbaker
dzbaker marked this pull request as ready for review September 28, 2026 12:18
@dzbaker
dzbaker marked this pull request as draft September 28, 2026 12:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CF_CFDP_R_ProcessFd does not bound received FD PDU offset against the declared transaction file size

2 participants