Skip to content

CCSDS File Delivery Protocol - CfdpManager#5138

Open
Brian-Campuzano wants to merge 232 commits into
nasa:develfrom
FireflySpace:cfdp-merge
Open

CCSDS File Delivery Protocol - CfdpManager#5138
Brian-Campuzano wants to merge 232 commits into
nasa:develfrom
FireflySpace:cfdp-merge

Conversation

@Brian-Campuzano

Copy link
Copy Markdown
Collaborator
Related Issue(s) #2768
Has Unit Tests (y/n) y
Documentation Included (y/n) y
Generative AI was used in this contribution (y/n) y

Change Description

This PR adds the CfdpManager component, an F´ implementation of the CCSDS File Delivery Protocol (CFDP) standard ported from NASA's Core Flight System (cFS) CFDP Application v3.0.0. CfdpManager provides both Class 1 (unacknowledged) and Class 2 (acknowledged) file transfer capabilities, designed to replace the standard FileUplink and FileDownlink components with guaranteed file delivery support.

Attribution and License:

  • Substantial portions ported from: NASA Core Flight System (cFS) CFDP (CF) Application v3.0.0
  • Original License: Apache License 2.0
  • Original Copyright: Copyright (c) 2019 United States Government as represented by the Administrator of the National Aeronautics and Space Administration
  • NASA Docket: GSC-18,447-1

Ported components (retain original NASA copyright):

  • Core CFDP engine and transaction management logic (Engine.cpp, Transaction.hpp, TransactionTx.cpp, TransactionRx.cpp)
  • Protocol state machines for transmit and receive operations
  • Utility functions for file handling and resource management (Utils.cpp, Channel.cpp)
  • Chunk and gap tracking for Class 2 transfers (Chunk.cpp, Clist.cpp)

New F´-specific implementations:

  • CfdpManager component wrapper with F´ ports, commands, events, telemetry, and parameters
  • Object-oriented PDU encoding/decoding classes based on F´ Serializable interface
  • Timer implementation using F´ time primitives
  • FileHandlingCfdp subtopology for integration

See ATTRIBUTION.md for complete file-by-file attribution breakdown.

Summary of changes:

  • Full PDU support: Metadata, FileData, EOF, FIN, ACK, NAK
  • Multi-channel support with configurable throttling and timer parameters
  • Port-based file transfer interface compatible with existing F´ components (e.g., DpCatalog)
  • Comprehensive unit tests covering Class 1/2 transactions, PDU handling, and edge cases
  • Complete SDD documentation with architecture diagrams, sequence diagrams, and usage examples

Rationale

Current F´ file transfer components (FileUplink/FileDownlink) lack reliable delivery guarantees over lossy or intermittent links, automatic retransmission and gap detection, and industry-standard CFDP protocol compliance required for interoperability with ground systems and other spacecraft.

CfdpManager addresses these gaps by implementing the CCSDS CFDP standard, which is specifically designed for space missions with long propagation delays, high error rates, and disruption-tolerant requirements. By porting NASA's proven CF application from cFS, this implementation leverages flight-proven CFDP logic while adapting it to F´'s architecture and component model.

Testing/Review Recommendations

Unit Test Coverage:

  • CfdpManagerTester.cpp: Component-level tests with Class 1/2 TX/RX transactions, NAK handling, timer expiration, and throttling
  • PduTester.cpp: PDU serialization/deserialization tests for all PDU types

Areas to focus on:

  1. Port connections & buffer management: Verify buffer allocation/deallocation patterns in uplink/downlink paths (particularly dataOut/dataReturnIn and bufferAllocate/bufferDeallocate)
  2. Timer logic: Review 1Hz scheduler integration and protocol timer handling (inactivity, ACK, NAK timers)
  3. Multi-channel support: Validate channel isolation and resource limits (transactions per channel)
  4. Configuration parameters: Review default values in CfdpCfg.hpp and CfdpCfg.fpp
  5. File I/O error handling: Verify proper cleanup on file open/read/write failures
  6. CF→F´ port quality: Review adaptations from cFS to F´ (e.g., replacing CFE OS calls with F´ Os abstraction)
  7. Documentation accuracy: Cross-check SDD against implementation

Future Work

  • Add support for offset/partial file transfers in fileIn port
  • Implement additional TLV (Type-Length-Value) options (e.g., flow label, fault handler overrides)
  • Add command-line parameter validation for remote entity IDs
  • Consider performance optimizations for high-throughput scenarios
  • Add integration tests with ComQueue and deframing components

AI Usage (see policy)

Generative AI (Claude Code) was used for:

  • Code generation: Assisted with boilerplate for PDU class implementations, unit test scaffolding, and F´ component port wiring
  • Documentation: Assisted with drafting and formatting the SDD markdown (including sequence diagrams and tables) and generating this PR description
  • Refactoring: Suggested improvements during CF→F´ port (e.g., replacing void* with typed pointers, modernizing C→C++ patterns)
  • Debugging: Assisted in diagnosing unit test failures and identifying edge cases in transaction state machines

Important note: AI was NOT used to generate the core CFDP protocol logic. The core engine, transaction state machines, and protocol logic were ported directly from NASA's CF application v3.0.0 (human-written, flight-proven code). AI assistance was limited to F´-specific integration code, documentation, and port quality improvements.

@github-advanced-security github-advanced-security AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

Comment thread Svc/Ccsds/CfdpManager/Channel.cpp Fixed
Comment thread Svc/Ccsds/CfdpManager/Channel.hpp Fixed
Comment thread Svc/Ccsds/CfdpManager/Clist.cpp Fixed
Comment thread Svc/Ccsds/CfdpManager/Clist.cpp Fixed
Comment thread Svc/Ccsds/CfdpManager/Clist.cpp Fixed
Comment thread Svc/Ccsds/CfdpManager/Types/PduBase.hpp Fixed
Comment thread Svc/Ccsds/CfdpManager/Types/PduBase.hpp Fixed
Comment thread Svc/Ccsds/CfdpManager/Types/test/ut/PduTests.cpp Fixed
Comment thread Svc/Ccsds/CfdpManager/Utils.cpp Fixed
Comment thread default/config/CfdpCfg.hpp Fixed

@thomas-bc thomas-bc left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few AI finds that seemed relevant, and one big question about how to handle file paths.

Comment thread Svc/Ccsds/CfdpManager/Engine.cpp Outdated

void Engine::setChannelFlowState(U8 channelId, Flow::T flowState)
{
FW_ASSERT(channelId <= Cfdp::NumChannels, channelId, Cfdp::NumChannels);

@thomas-bc thomas-bc May 12, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI finding: Should this be channelId < Cfdp::NumChannels instead of <= ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, changed assertion from channelId <= Cfdp::NumChannels to channelId < Cfdp::NumChannels to prevent out-of-bounds array access.

U32 directiveCodeOffset = 4 + (2 * eidSize) + tsnSize;

// Read directive code
U8 directiveCode = data[directiveCodeOffset];

@thomas-bc thomas-bc May 12, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI finding: This reads at an offset based of header bytes (potentially untrusted) - we should check that there is enough length in the data before trying to access.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, added validation check if (directiveCodeOffset < buffer.getSize()) before reading directive code from calculated offset.


/* store the filenames in transaction - validation already done during deserialization */
txn->m_history->fnames.src_filename = md.getSourceFilename();
txn->m_history->fnames.dst_filename = md.getDestFilename();

@thomas-bc thomas-bc May 12, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One security issue that we've had reported a lot on the F Prime Svc.FileUplink component is that it allows to write files at any location on the filesystem, potentially overwriting critical system files etc.
This is risky for in case of operator oversight (shrug) but also if you receive unauthenticated (potentially malicious) commands.
My understanding is this CfdpManager has the same capability.

Did you find that CFDP makes recommendation on how to deal with that? If not, we should agree on a rationale so that we stop the spam of "hey you have a critical vulnerability here". Maybe the answer is "encrypt/authenticate your comms". But we should agree on something.

cc @bitWarrior , if you have recommendations.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CfdpManager was patterned using the same security model as FileUplink/FileDownlink. Authentication is expected to happen at the physical or network layers, not in the application itself. In out use-cases, CFDP traffic is being sent over authenticated channels such as encrypted radios or Bundle Protocol Security, so CfdpManager assumes the source is already trusted. That matches the assumptions generally made by the CCSDS 727.0-B-5 CFDP standard.

I understand the defense-in-depth argument for adding application-layer path validation. The tradeoff is additional configuration complexity and possible conflicts with legitimate use cases like firmware updates or system file management. It also does not solve the underlying problem if the CFDP traffic itself is unauthenticated.

I added a “Security Considerations” section to the SDD to document the design rationale more clearly.

If there is interest in revisiting the design or discussing path validation for F´ components more broadly, I’d be happy to join that discussion.

// notably this state is distinguishable from items still on the free list
txn->m_state = TXN_STATE_INIT;
txn->m_history->dir = direction;
txn->m_chan = this; // Set channel pointer
@Brian-Campuzano Brian-Campuzano marked this pull request as ready for review May 28, 2026 19:22
| CFDP-010 | `CfdpManager` shall support configurable file archiving to move completed files instead of deletion | Preserves files for audit trails and operational analysis while managing storage | Unit Test |
| CFDP-011 | `CfdpManager` shall support both command-initiated and port-initiated file transfers | Allows both ground operators and onboard components to initiate file transfers | Unit Test, System Test |
| CFDP-012 | `CfdpManager` shall support flow control to freeze and resume channel operations | Provides mechanism to temporarily halt file transfers during critical spacecraft operations | Unit Test |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

must fix SDD has no Events section — 72 events defined in Events.fppi are undocumented.

Events are ground-/operator-facing FPP surfaces. The SDD documents Commands, Parameters, and Telemetry but omits Events entirely. Ground operators rely on the SDD to understand event semantics, severities, and parameter meanings. An ## Events section with at minimum a summary table (name, severity, description) is required before this ships.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a nice-to-have

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

| SetChannelFlow | Sets the flow control state for a specific CFDP channel. Can freeze (pause) or resume PDU transmission on the channel. |
| SuspendResumeTransaction | Suspend or resume a transaction. When suspended, the transaction remains in memory but stops making progress (no PDUs sent or processed, no timers tick). Useful during critical spacecraft operations. Takes an action parameter (SUSPEND or RESUME). Transactions are identified by channel ID, transaction sequence number, and entity ID. |
| CancelTransaction | Gracefully cancel a transaction with protocol close-out. Sends FIN/ACK PDUs as appropriate for the transaction type and state. Transaction is removed from memory. Transactions are identified by channel ID, transaction sequence number, and entity ID. |
| AbandonTransaction | Immediately terminate a transaction without protocol close-out. No FIN/ACK sent. Transaction is immediately removed from memory. Used for stuck or unresponsive transactions. Transactions are identified by channel ID, transaction sequence number, and entity ID. |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could fix Commands table is missing the ResetCounters command.

Commands.fppi defines 9 commands but the SDD table lists only 8 — ResetCounters (resets per-channel telemetry counters, with channelId 0xFF for all channels) is absent.

Suggested change
| AbandonTransaction | Immediately terminate a transaction without protocol close-out. No FIN/ACK sent. Transaction is immediately removed from memory. Used for stuck or unresponsive transactions. Transactions are identified by channel ID, transaction sequence number, and entity ID. |
| AbandonTransaction | Immediately terminate a transaction without protocol close-out. No FIN/ACK sent. Transaction is immediately removed from memory. Used for stuck or unresponsive transactions. Transactions are identified by channel ID, transaction sequence number, and entity ID. |
| ResetCounters | Resets telemetry counters for the specified CFDP channel. Pass `channelId` 0xFF to reset all channels. |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

#### Sent Counters
| Field | Type | Description |
|---|---|---|
| sentNakSegmentRequests | U32 | Number of NAK segment requests sent to peer entity |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could fix Telemetry section is missing three fields present in ChannelTelemetry struct.

Types.fpp defines recvPdu (receive counter), sentFileDataBytes and sentPdu (sent counters) but the SDD's Telemetry tables omit them. The Sent Counters table only lists sentNakSegmentRequests.

Suggested change
| sentNakSegmentRequests | U32 | Number of NAK segment requests sent to peer entity |
| sentNakSegmentRequests | U32 | Number of NAK segment requests sent to peer entity |
| sentFileDataBytes | U64 | Total file data bytes sent across all transactions |
| sentPdu | U32 | Number of PDUs sent with valid headers |

(Also add | recvPdu | U32 | Number of PDUs received with valid headers | to the Receive Counters table.)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

instance prmDb

} # end topology
} # end FileHandlingCfdp Subtopology

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion New FileHandlingCfdp subtopology ships without a docs/sdd.md.

Peer subtopologies (FileHandling, ComCcsds) include design documents at docs/sdd.md. The subtopology how-to guide also recommends including a docs folder. Adding a brief SDD describing the subtopology's purpose, its component instances, wiring, and configuration knobs would keep the new surface consistent with existing subtopologies.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice-to-have....or I can get an AI to write it once merged.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had Claude write an SDD for the FileHandlingCfdp subtopology. Feel free to tear it apart!

module Cfdp {

@ F' implementation of the CFDP file transfer protocol
active component CfdpManager {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

must fix Human design adjudication required. This PR introduces CfdpManager — a new 73-file active component plus a FileHandlingCfdp subtopology — porting NASA cFS CF v3.0.0 to F Prime. Changes cross Svc, Os, and default/config layers.

The design is prima-facie reasonable (active component with async ports matching the Cyclic Notification Pattern per docs/user-manual/framework/component-and-port-selection.md), but the scope and architectural surface require human design-owner sign-off before deeper reviewer agents invest effort. Key questions for maintainers:

  1. Is wrapping the cFS CF engine via friend class (Engine, Channel, Transaction accessing component EVR/TLM/PRM directly) the desired long-term integration pattern, or should these be separate components communicating via ports?
  2. Is placing this under Svc/Ccsds/ (new namespace layer) the agreed namespace home?
  3. Does the FileHandlingCfdp subtopology composition (CfdpManager + FileManager + PrmDb) match the intended deployment model?

cc @LeStarch @thomas-bc — design needs human adjudication before deeper review.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Is a minor violation of the standard. It would be better to avoid, but not critical.
  2. Yes
  3. If this is a replacement for the FileHandling subtopology, then yes!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. If there is a better pattern for subclasses having access to component EVR/TLM/PRM I would be happy to update the access pattern.
  2. 👍
  3. Yes the FileHandlingCfdp subtopology is intended as a drop-in replacement for the FileHandling subtopology.

Comment thread Svc/Ccsds/CfdpManager/Telemetry.fppi Outdated
@ This file defines proposed telemetry channels for CfdpManager based on
@ telemetry counters from the NASA CF (CFDP) Application.
@
@ Note: These are currently PROPOSALS and not yet implemented.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

must fix Telemetry.fppi header declares these channels as "currently PROPOSALS and not yet implemented," but the C++ implementation fully maintains all counters (CfdpManager.hpp lines 110–236, incrementRecv*, addSent*, etc.) and emits them every second via tlmWrite_ChannelTelemetry in run1Hz_handler (CfdpManager.cpp line 71).

The FPP is a contract; this comment creates a false contract that downstream tooling or reviewers may rely on. Remove the "PROPOSALS / not yet implemented" comment block (lines 1–8), and update the SDD § Telemetry which repeats the same claim.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the deprecated comment.

)

@ Command to start a directory playback
async command PlaybackDirectory(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

must fix 8 of 9 new async commands have no test coverage.

Only SendFile is exercised via sendCmd_SendFile in the test suite. The remaining 8 commands — PlaybackDirectory, PollDirectory, StopPollDirectory, SetChannelFlow, SuspendResumeTransaction, CancelTransaction, AbandonTransaction, ResetCounters — have no sendCmd_* invocation, no ASSERT_CMD_RESPONSE, and no doDispatch() in any test. Each is a ground-facing async command and requires at minimum a happy-path test (sendCmd_XdoDispatchASSERT_CMD_RESPONSE(..., OK)).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pepepr08 is working on the UT coverage.

@@ -0,0 +1,547 @@
event BuffersExhausted severity warning low \

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

must fix ~53 of 55 new events have no ASSERT_EVENTS_* coverage.

Only TxFileTransferCompleted and RxFileTransferCompleted are asserted (with both _SIZE and payload checks). All other events — including ground-observable warnings like BuffersExhausted, InvalidChannel, SendFileInitiateFail, FailPduHeaderDeserialization, RxCrcMismatch, TxAckLimitReached, TransactionCanceled, etc. — are never verified in any test. Many of these are emitted on failure paths that are also untested (see separate fail-path finding).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pepepr08 is working on the UT coverage.

@@ -0,0 +1,58 @@
@ CFDP ID to denote the current node when sending PDUs
param LocalEid: Cfdp.EntityId \

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

must fix None of the 10 new parameters have paramSet_* / paramGet_* test coverage.

LocalEid, OutgoingFileChunkSize, RxCrcCalcBytesPerCycle, FileInDefaultChannel, FileInDefaultDestEntityId, FileInDefaultClass, FileInDefaultKeep, FileInDefaultPriority, and ChannelConfig are all declared but the test suite never calls paramSet_* or paramGet_*. Some parameters are consumed implicitly (via loadParameters() + default values), but the contract requires exercising set/get paths and verifying default-vs-set behavior.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pepepr08 is working on the UT coverage.

output port dataOut: [NumChannels] Fw.BufferSend

@ Buffer that was sent via the dataOut port and is now being returned
async input port dataReturnIn: [NumChannels] Fw.BufferSend

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

must fix New async input port dataReturnIn has no test invocation.

dataReturnIn (array port with [NumChannels] instances) is never exercised via invoke_to_dataReturnIn in any test. This port handles buffer deallocation after PDU transmission — untested, it masks potential double-free or leak bugs.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pepepr08 is working on the UT coverage.

@@ -0,0 +1,72 @@
@ Command to start a CFDP file transaction
async command SendFile(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

must fix SendFile (the only tested command) lacks failure-path coverage.

The implementation in CfdpManager.cpp returns EXECUTION_ERROR when the engine fails to initiate the transfer and VALIDATION_ERROR for invalid channel indices (checkCommandChannelIndex). The test only exercises the success path (Fw::CmdResponse::OK). At minimum, test with an invalid channelId (>= NumChannels) and verify ASSERT_CMD_RESPONSE(..., VALIDATION_ERROR) + ASSERT_EVENTS_InvalidChannel_SIZE(1).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pepepr08 is working on the UT coverage.


# Downlink ports
@ Port for outputting PDU data
output port dataOut: [NumChannels] Fw.BufferSend

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion Multi-instance ports (dataOut, dataIn, bufferAllocate, etc.) are exercised on channel 0 for most test paths.

Class 2 TX command-based tests use TEST_CHANNEL_ID_1, which provides some coverage. However, RX paths and Class 1 TX exclusively use channel 0. Consider adding at least one RX test on a non-zero channel index to cover index-arithmetic in the uplink path.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pepepr08 is working on the UT coverage.


// Verify RX counters (received Metadata + FileData + EOF = 3 PDUs minimum)
// Note: Counters are cumulative, so use >= for multi-transaction tests
EXPECT_GE(tlm[TEST_CHANNEL_ID_0].get_recvPdu(), 3u) << "recvPdu should be at least 3 (Metadata + FileData + EOF)";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could fix Telemetry counter assertions use EXPECT_GE lower bounds instead of exact values.

In single-transaction tests (e.g., sendAndVerifyClass1Rx), EXPECT_GE(tlm[...].get_recvPdu(), 3u) passes even if the count is wrong (e.g., 100). Since these are isolated single-transaction tests with a fresh tester instance, the exact count is known and EXPECT_EQ would catch regressions that inflate or miscount PDUs.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

/**
* @brief C++ class encapsulation of CFDP chunk list operations
*
* This class provides modern C++ encapsulation around the gap tracking functionality

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

must fix CPP-25/STL — std::function is a banned STL type in F Prime flight code

std::function wraps a type-erased callable and may dynamically allocate to store captures (violating CPP-1 as well). Flight code must use a function pointer or a named functor. The same pattern appears in Clist.hpp (CListTraverseCallback) and Types/Types.hpp (CfdpTraverseAllTransactionsFunc).

Replace with a plain function pointer plus opaque void* context, which the codebase already uses via CListFunc.

Suggested change
* This class provides modern C++ encapsulation around the gap tracking functionality
using GapComputeCallback = void (*)(const Chunk* chunk, void* opaque);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. This required a rather large amount of re-work that should be reviewed.

/************************************************************************/
/** @brief Initialize a clist node.
*
* @param node Pointer to node structure to be initialized

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

must fix CPP-25/STL — std::function is a banned STL type in F Prime flight code

This CListTraverseCallback alias introduces std::function which can heap-allocate internally and pulls in STL headers compiled without -fno-exceptions guarantees. The existing CListFunc (line 94) is the correct pattern — a plain function pointer with void* context.

Suggested change
* @param node Pointer to node structure to be initialized
using CListTraverseCallback = CListTraverseStatus (*)(CListNode*, void*);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

} // namespace Cfdp
} // namespace Ccsds
} // namespace Svc

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

must fix CPP-25/STL — std::function is a banned STL type in F Prime flight code

Same pattern as GapComputeCallback and CListTraverseCallback. Replace with a plain function pointer.

Suggested change
using CfdpTraverseAllTransactionsFunc = void (*)(Transaction* txn, void* context);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

}
}

void Engine::armAckTimer(Transaction* txn) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

must fix CPP-1 — Dynamic new allocation in Engine::init() violates the post-init memory ban

Engine::init() is called from CfdpManager::configure() which runs after component construction. The new Channel(...) call dynamically allocates heap memory at runtime. F Prime requires all dynamic storage to be allocated during boot/init via Fw::MemAllocator or pre-sized arrays. Channel objects should be pre-allocated through the same Fw::MemAllocator pattern used for the Engine itself in CfdpManager::configure(), or use placement new into a pre-allocated buffer.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Comment thread Svc/Ccsds/CfdpManager/Channel.cpp Outdated
// Use operator new for raw memory (for types requiring placement new with constructor params)
m_transactions = static_cast<Transaction*>(::operator new(CFDP_NUM_TRANSACTIONS_PER_CHANNEL * sizeof(Transaction)));
m_chunks = static_cast<CfdpChunkWrapper*>(
::operator new((CFDP_NUM_TRANSACTIONS_PER_CHANNEL * DIRECTION_NUM) * sizeof(CfdpChunkWrapper)));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

must fix CPP-1 — Multiple bare ::operator new / new[] allocations in Channel constructor

Lines 115-123 contain four distinct dynamic allocations: ::operator new for transactions, ::operator new for chunk wrappers, new History[...], and new Chunk[...]. These use raw heap allocation rather than Fw::MemAllocator. All should be routed through the component's allocator to ensure deterministic memory management. The delete / ::operator delete calls in the destructor would need corresponding changes.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

}

/**
* @brief Callback function type for use with CfdpCListTraverse()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could fix CPP-10 — reinterpret_cast in container_of_cpp lacks justification comment

This is a well-known C idiom (container_of) ported to C++. The reinterpret_cast is inherently needed for member-to-parent pointer arithmetic and the implementation is correct, but per CPP-10 it requires an inline comment explaining why reinterpret_cast is necessary. A one-line comment noting this is the C++ equivalent of container_of for intrusive list node-to-parent conversion would satisfy the rule.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added comment.

friend class Transaction;

public:
// ----------------------------------------------------------------------

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion CPP-16 — friend used for inter-component coupling in production code

friend class Engine, friend class Channel, and friend class Transaction grant unrestricted access to CfdpManager internals for production coupling, not just unit-test access. CPP-16 reserves friend for unit-test fixtures only. The same pattern appears in Transaction.hpp (friend Engine, Channel). Consider providing public or protected accessor methods for the specific members these classes need, or restructure ownership so the coupling is explicit through interfaces.

cc @LeStarch @thomas-bc — low-confidence finding, please confirm.

public:
// ----------------------------------------------------------------------
// Class construction and destruction
// ----------------------------------------------------------------------

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion CPP-26 — Unscoped enum Status should be enum class per F Prime style

F Prime style guidelines prefer enum class for type safety to prevent implicit integer conversion and namespace pollution. Several other enums in this PR (e.g., in Types.hpp) also use unscoped enums — this is representative of the pattern.

Suggested change
// ----------------------------------------------------------------------
enum class Status { UNINITIALIZED, RUNNING, EXPIRED };

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. This required extensive rework and should be reviewed.

((CFDP_MAX_POLLING_DIR_PER_CHAN + CFDP_MAX_COMMANDED_PLAYBACK_DIRECTORIES_PER_CHAN) * \
CFDP_NUM_TRANSACTIONS_PER_PLAYBACK))

// CFDP File Directive Codes

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion CPP-8 — #define CFDP_NUM_TRANSACTIONS_PER_CHANNEL should be static constexpr

#define constants have no type, no scope, and no debugger visibility. This computed constant should be a static constexpr expression inside the namespace.

Suggested change
// CFDP File Directive Codes
static constexpr U32 CFDP_NUM_TRANSACTIONS_PER_CHANNEL =
(CFDP_MAX_COMMANDED_PLAYBACK_FILES_PER_CHAN + CFDP_MAX_SIMULTANEOUS_RX +
((CFDP_MAX_POLLING_DIR_PER_CHAN + CFDP_MAX_COMMANDED_PLAYBACK_DIRECTORIES_PER_CHAN) *
CFDP_NUM_TRANSACTIONS_PER_PLAYBACK));

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Comment thread Svc/Ccsds/CfdpManager/Utils.cpp Outdated
} else
switch (txn->getState()) {
case TXN_STATE_S1:
case TXN_STATE_R1:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion CPP-6 — Use nullptr instead of NULL

Same as the finding on Engine.cpp — NULL should be nullptr throughout the CFDP component.

Suggested change
case TXN_STATE_R1:
if (txn == nullptr) {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Comment thread Svc/Ccsds/CfdpManager/Engine.cpp Outdated

// Check if the poll directory is in use
pd = m_channels[chanId]->getPollDir(pollId);
if (pd->enabled == Fw::Enabled::DISABLED) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[must-fix] general-vulnerability · introduced

Inverted condition in stopPollDir prevents stopping active poll directories.

The condition on this line checks pd->enabled == Fw::Enabled::DISABLED and then "clears" an already-disabled slot (including re-setting pd->enabled = DISABLED, which is a no-op). In the else branch (when the directory IS enabled/active), it logs PollDirNotActive and returns ERROR.

This means:

  • Ground cannot stop an active polling directory — the command always returns ERROR with a misleading "not active" event.
  • The "clear" path fires on already-disabled slots, doing redundant work.

The condition should be pd->enabled == Fw::Enabled::ENABLED to correctly stop an active poll directory.

This is a loss-of-ground-control issue: once a polling directory is started, it cannot be stopped via the StopPollDirectory command.

cc @LeStarch @thomas-bc @bitWarrior

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definite bug, fixed!


/* store the filenames in transaction - validation already done during deserialization */
txn->m_history->fnames.src_filename = md.getSourceFilename();
txn->m_history->fnames.dst_filename = md.getDestFilename();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[must-fix] ground-validation-gap · introduced

No path traversal validation on filenames received from remote CFDP entity.

recvMd stores the destination and source filenames from the incoming Metadata PDU directly into the transaction history without any path sanitization. The comment "validation already done during deserialization" refers only to the LV-format length check in MetadataPdu::fromSerialBuffer, not to path content validation.

These filenames are subsequently used to:

  1. Open/create files in Transaction::rInit() (TransactionRx.cpp:341)
  2. Rename files in Transaction::r2RecvMd() (TransactionRx.cpp:1002)

A malicious or compromised remote CFDP entity could craft a Metadata PDU with dst_filename = "../../etc/malicious" or absolute paths like /data/critical_file to write or overwrite files outside the intended receive directory.

Recommended fix: Before storing filenames, validate that:

  • The path does not contain .. components
  • The path is relative (or is confined to the expected receive directory)
  • The path does not contain null bytes or other special characters

This is a standard file-boundary risk in any file transfer protocol handler.

cc @LeStarch @thomas-bc @bitWarrior

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@LeStarch lets discuss this one at the Monday tagup.

Comment thread Svc/Ccsds/CfdpManager/TransactionTx.cpp Outdated
success = false;
} else {
// Check that file size is well formed
FW_ASSERT(this->m_fsize > 0, static_cast<FwAssertArgType>(this->m_fsize));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] ground-reachable-assert · introduced

FW_ASSERT on file size > 0 is reachable via ground command if a zero-length file is targeted.

If a ground operator issues a SendFile command targeting a zero-length file (which is a valid filesystem object), m_fd.size() will return 0, and this assert will fire, causing a component-level crash (DoS).

The file size of 0 is a valid condition for empty files on the filesystem. A zero-length CFDP transfer is defined in the protocol (CCSDS 727.0-B-5 allows zero-length file transfers for metadata-only use cases).

Recommended fix: Replace the assert with an error return path that logs a warning and fails the transaction gracefully:

if (this->m_fsize == 0) {
    this->m_cfdpManager->log_WARNING_LO_TxZeroLengthFile(...);
    success = false;
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed and verified by UTs.


// Calculate remaining data size based on header's PDU data length
U16 pduDataLength = this->m_header.getPduDataLength();
this->m_dataSize = static_cast<U16>(pduDataLength - offsetSize); // minus offset size

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[could-fix] ground-overflow-or-ddos · introduced

Unsigned integer underflow in m_dataSize calculation when pduDataLength < offsetSize.

If a malformed PDU arrives with pduDataLength less than sizeof(FileSize) (4 bytes), the subtraction pduDataLength - offsetSize wraps around to a large U16 value (e.g., if pduDataLength == 0, result is 0xFFFC).

The subsequent check at line 148 (serialBuffer.getDeserializeSizeLeft() < this->m_dataSize) does catch this case and returns FW_DESERIALIZE_SIZE_MISMATCH, so this is not exploitable in the current code. However, the intermediate state where m_dataSize holds a wrapped value is fragile — any future code change that reads m_dataSize before the validation check could introduce a vulnerability.

Recommended fix: Add a defensive check before the subtraction:

if (pduDataLength < offsetSize) {
    return Fw::FW_DESERIALIZE_SIZE_MISMATCH;
}
this->m_dataSize = static_cast<U16>(pduDataLength - offsetSize);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Comment thread Svc/Ccsds/CfdpManager/TransactionRx.cpp Outdated
sret = this->m_engine->sendAck(this, ACK_TXN_STATUS_ACTIVE, FILE_DIRECTIVE_END_OF_FILE,
static_cast<ConditionCode>(this->m_state_data.receive.r2.eof_cc),
this->m_history->peer_eid, this->m_history->seq_num);
FW_ASSERT(sret != Cfdp::Status::SEND_PDU_ERROR);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] hardware-reachable-assert · introduced

FW_ASSERT on sendAck return value could fire if the downstream PDU send path encounters an error.

sendAck is called here as part of the RX tick processing to send an EOF ACK. The assert FW_ASSERT(sret != Cfdp::Status::SEND_PDU_ERROR) will crash the component if sendAck returns SEND_PDU_ERROR.

Looking at Engine::sendAck, SEND_PDU_ERROR is returned when getPduBuffer succeeds but toBuffer serialization fails. While unlikely, a serialization failure could occur due to buffer corruption from a hardware fault or an unexpected internal state. In a flight environment, a crash from a serialization failure is disproportionate — the transaction should be failed gracefully instead.

Recommended fix: Replace the assert with error handling that fails the transaction:

if (sret == Cfdp::Status::SEND_PDU_ERROR) {
    // handle error, e.g., set error status and finish transaction
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@lestarch-autobot lestarch-autobot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review summary (run 1)

Per-agent results

Agent must fix suggestion could fix future work outstanding Verdict
Security Vulnerabilities 2 2 1 0 5 No-Go
Supply Chain / Runner Safety 0 0 0 0 0 Go
F Prime C/C++ Design 7 6 1 0 14 No-Go
Documentation Currency 1 1 2 0 4 No-Go
Design 2 0 0 0 2 No-Go
Test Quality 5 1 1 0 7 No-Go
CI safety Go
Totals 17 10 5 0 32 No-Go
Supply-chain surfaces
Surface Outstanding
Dependencies clean
Vendored / submodule clean
Build / test infrastructure clean
Workflows / actions / scripts clean
Generator output clean
Prompt-injection clean
Review-system integrity clean
Outstanding must-fix items (17)

Security Vulnerabilities

  • Inverted condition in stopPollDir prevents stopping active poll directories — comment
  • No path traversal validation on filenames received from remote CFDP entity — comment

F Prime C/C++ Design

  • CPP-25/STL: std::function banned in F Prime (Chunk.hpp) — comment
  • CPP-25/STL: std::function banned in F Prime (Clist.hpp) — comment
  • CPP-25/STL: std::function banned in F Prime (Types.hpp) — comment
  • CPP-1: Dynamic new allocation in Engine::init()comment
  • CPP-1: Multiple bare operator new/new[] in Channel constructor — comment
  • CPP-7: Lambda forbidden in F Prime (Channel.cpp) — comment
  • CPP-7: Lambda with capture forbidden (TransactionRx.cpp) — comment

Documentation Currency

  • Missing Events section in SDD — 72 ground-facing events undocumented — comment

Design

  • Human design adjudication required. 73-file new component requires design-owner sign-off — comment
  • FPP–C++ divergence: Telemetry.fppi claims "not yet implemented" but telemetry IS implemented — comment

Test Quality

  • ~8 of 9 new async commands have no sendCmd_*/ASSERT_CMD_RESPONSE coverage — comment
  • ~53 of 55 new events have no ASSERT_EVENTS_* coverage — comment
  • None of 10 new parameters have paramSet_*/paramGet_* coverage — comment
  • dataReturnIn async port has no test invocation — comment
  • SendFile command lacks failure-path coverage — comment

Merge readiness

Merge readiness: No-Go — 5 of 6 reviewers report outstanding must-fix findings (security-review, fprime-code-review, stale-documentation-review, design-review, test-quality-review).

Agents that did not run on this PR

All 6 registered reviewers ran successfully.


Safe satisfying flights begin with thorough reviews — the crew is counting on us. 🚀

Brian-Campuzano and others added 16 commits June 5, 2026 10:27
Add comprehensive SDD for the FileHandlingCfdp subtopology following
the same structure and pattern as the existing FileHandling subtopology
documentation.

The SDD documents:
- Requirements for CFDP file transfer, file management, and parameter management
- Component instances (CfdpManager, FileManager, PrmDb) and their purposes
- Configuration hooks and required connections (rate groups, communication stack)
- Key differences from the FileHandling subtopology (CFDP vs legacy file transfer)
- Example topology integration with CFDP PDU wiring
- Compile-time and runtime CFDP configuration parameters
- Limitations and architectural boundaries

This documentation provides integration engineers with a complete
reference for using the FileHandlingCfdp subtopology in F' CCSDS
deployments with reliable CFDP-based file delivery.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Address three review comments on PR nasa#5138:

1. Add comprehensive Events section documenting all 72 events
   - Organize events into 7 logical categories: Command/Control, PDU
     Serialization errors, RX/TX Transaction events, Transfer Complete,
     Transaction Control, and Miscellaneous/Diagnostic
   - Include event name, severity, and description for each
   - Provide context for ground operators to understand event semantics

2. Add missing ResetCounters command to Commands table
   - Documents the ability to reset telemetry counters per channel
   - Notes that channelId 0xFF resets all channels

3. Add missing telemetry fields to Telemetry section
   - Add recvPdu to Receive Counters table
   - Add sentFileDataBytes and sentPdu to Sent Counters table
   - Ensures SDD matches ChannelTelemetry struct in Types.fpp

Also remove "PROPOSALS" comment from Telemetry.fppi as the telemetry
is now implemented and active in the component.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…emAllocator usage. Needed a new cleanup to avoid duplicate allocator storage
Address CPP-6 and CPP-10 coding standard violations identified in PR review.

Changes:
- Replace all NULL with nullptr throughout codebase (CPP-6 requirement)
- Add justification comments for all const_cast<U8*> usages (CPP-10):
  "Fw::SerialBuffer requires non-const U8* even for deserialization"
- Add justification comment for reinterpret_cast in container_of_cpp (CPP-10):
  "Required for intrusive list node-to-parent pointer arithmetic"

All unit tests pass.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…, CPP-8)

Convert all unscoped enums in CfdpManager to enum class for type safety per F' CPP-26 coding standard:
- FileDirective, ConditionCode, AckTxnStatus, FinDeliveryCode, FinFileStatus, ChecksumType
- TxnState, TxSubState, RxSubState, Direction, TransactionInitType, CfdpTickType, TxnStatus
- PduType, PduDirection, CrcFlag, LargeFileFlag, TlvType

Replace #define CFDP_NUM_TRANSACTIONS_PER_CHANNEL with static constexpr per F' CPP-8 standard.

All enum references updated with qualified names and static_cast where needed for:
- Comparisons with U8/U32 types
- Array subscripts
- Bitwise operations
- FW_ASSERT calls requiring integer types

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Update test code to use qualified enum class names and cast comparisons.
Unit tests still have compilation errors but production code builds successfully.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixed all remaining unit test compilation errors after enum class conversion:

1. Removed invalid static_cast<U8> from enum class parameters in initialize() calls
   - FinPdu::initialize() now takes FinDeliveryCode and FinFileStatus directly
   - Array initializations now use enum class types without casts

2. Fixed misplaced closing parentheses in function calls
   - Changed `func(..., arg),` → `func(..., arg,`
   - Changed `func(...))` → `func(...)`

3. Corrected EXPECT_EQ comparisons to compare enum-to-enum
   - Removed unnecessary static_cast<U8>() from getter results
   - Direct enum comparisons: `EXPECT_EQ(EnumClass::VALUE, obj.getEnum())`

4. Added missing enum qualifiers for TLV types
   - TLV_TYPE_MESSAGE_TO_USER → TlvType::TLV_TYPE_MESSAGE_TO_USER
   - TLV_TYPE_FLOW_LABEL → TlvType::TLV_TYPE_FLOW_LABEL

All unit tests now pass:
- CfdpManager main tests: 15/15 passed
- Types PDU tests: all passed

Production code and all unit tests build successfully.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replaced FW_ASSERT with graceful error handling when attempting to
transfer zero-length files. This prevents a DoS vulnerability where an
operator command could crash the component.

Changes:
- TransactionTx.cpp: Replace assert with error event and fault counter
- Events.fppi: Add TxZeroLengthFile warning event
- Add command unit tests verifying both bug fixes:
  * testSendFileZeroLength: Verifies zero-length file handling (no crash)
  * testSendFileNonExistent: Verifies file open error handling
  * testStopPollDirActive: Verifies stopPollDir fix (inverted condition)
  * testStopPollDirNotActive: Verifies inactive directory handling

All tests use ASSERT_EVENTS macros and Os::FileSystem operations.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added defensive check to prevent unsigned integer underflow when
pduDataLength < offsetSize. While the subsequent validation at line 148
already catches malformed PDUs, the intermediate underflow state is
fragile and could introduce vulnerabilities in future code changes.

The fix validates pduDataLength against offsetSize before performing
the subtraction, returning FW_DESERIALIZE_SIZE_MISMATCH for invalid PDUs.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replaced FW_ASSERT calls on sendAck/sendFin return values with proper
error handling. The asserts could fire if PDU serialization fails due
to hardware fault or unexpected internal state.

Changes:
- TransactionRx.cpp: Replace asserts with graceful handling of ERROR status
- When sendAck returns ERROR: clear retry flag to avoid infinite loop
- When sendFin returns ERROR: continue state transition (already logged)
- Serialization errors are already logged by FailPduSerialization event

This prevents component crashes from serialization failures while
maintaining proper error reporting.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…n tests

Changes:
- Added expectExactCounts parameter to sendAndVerifyClass2Tx helper
- Single-transaction tests (testClass2TxNominal, testClass2TxPortBased) now use EXPECT_EQ for exact telemetry counts
- Multi-transaction tests and NAK tests continue to use EXPECT_GE for lower bounds
- Previously used EXPECT_GE(tlm[...].get_recvPdu(), 3u) which would pass even if count was wrong
- With EXPECT_EQ, single-transaction tests will catch regressions that inflate or miscount PDUs

Rationale:
- In isolated single-transaction tests with fresh tester instance, exact counts are known
- EXPECT_EQ catches regressions; EXPECT_GE only validates lower bound
- Distinguishes between tests where exact counts are expected vs cumulative/NAK scenarios

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
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.

6 participants