Skip to content

[SECURITY] DoS via Invalid Memory Access in cFS CS Application #945

Open
nasa/CS
#122

Description

@k-spacesec

1. Describe the bug

A Denial of Service (DoS) vulnerability exists in the Checksum (CS) application of the NASA core Flight System (cFS). The issue stems from insufficient memory address validation in the CS_OneShotCmd() function.

While the application uses CFE_PSP_MemValidateRange() to check user-supplied addresses, this function may return CFE_SUCCESS for non-existent or invalid memory regions depending on the PSP implementation. Consequently, the child task CS_OneShotChildTask() attempts to calculate a CRC on an invalid pointer, leading to a Segmentation Fault and the abnormal termination of the CS application.

2. System observed on

Hardware: x86_64 / ARM (Generic)
OS: Linux
Versions: cFE (core Flight Executive) & CS Application (Latest versions)
Component: Checksum (CS) Application

3. Vulnerability Analysis

3.1. Technical Details

The vulnerability occurs during the processing of a "OneShot" checksum command.

  1. Inadequate Validation: In CS_OneShotCmd(), the payload address and size are validated using CFE_PSP_MemValidateRange(). In many environments, this function lacks strict boundary checks for the entire physical/virtual memory map.
Image
  1. Pointer Dereference in Child Task: The validated (but actually invalid) address is stored in CS_AppData.HkPacket.Payload.LastOneShotAddress and passed to the child task.
  2. The Crash: Inside CS_OneShotChildTask(), the function calls CFE_ES_CalculateCRC() with the unverified address. When CFE_ES_CalculateCRC() attempts to read from this memory location, the system triggers a segmentation fault.

3.2. Code Snippets

Location: CS_OneShotCmd()

void CS_OneShotCmd(const CS_OneShotCmd_t *CmdPtr)
{
    CFE_Status_t Status;
    /* Insufficient validation: returns CFE_SUCCESS for some invalid addresses */
    Status = CFE_PSP_MemValidateRange(CmdPtr->Payload.Address, CmdPtr->Payload.Size, CFE_PSP_MEM_ANY);

    if (Status == CFE_SUCCESS) {
        /* Proceed to spawn child task with invalid Address */
    }
}

Location: CS_OneShotChildTask()

void CS_OneShotChildTask(void)
{
    cpuaddr FirstAddrThisCycle = CS_AppData.HkPacket.Payload.LastOneShotAddress;
    
    // ...
    /* CRASH: Direct memory access to invalid FirstAddrThisCycle */
    NewChecksumValue = CFE_ES_CalculateCRC((void *)(FirstAddrThisCycle), 
                                           NumBytesThisCycle, 
                                           NewChecksumValue,
                                           CS_DEFAULT_ALGORITHM);
    // ...
}

4. To Reproduce (PoC)

  1. Preparation: Deploy the cFS framework with the CS application enabled.
  2. Craft Packet: Create a CS_OneShotCmd ground command packet.
  3. Inject Payload: Set the Address field to a known non-mapped memory address (e.g., 0xDEADBEEF) and a valid Size (e.g., 100).
  4. Send Command: Dispatch the command to the cFS software bus.
  5. Observe: The CS child task will trigger a Segmentation Fault, causing the CS application to crash or the OS to terminate the process.
Image

5. Expected behavior

The CS_OneShotCmd() should perform a more rigorous check against the actual available memory map. If an address is outside the permitted operational memory range, the command should be rejected with an error telemetry packet, and no child task should be spawned.

6. Mitigation & Recommendations

  • Enhanced PSP Validation: Strengthen the logic within CFE_PSP_MemValidateRange() to strictly adhere to the specific hardware's memory map.
  • Application-Level Bounds Checking: Implement a secondary check within the CS application that compares the requested address against a "Safe Memory Table" defined for the specific mission.
  • Error Handling: Implement robust exception handling or memory access guards (if supported by the OSAL/PSP) within the CRC calculation loop to prevent a full application crash.

Reporter Info
Space Cybersecurity Council

Activity

  1. Shrey-N commented on Mar 7, 2026

    @Shrey-N

    Proposed Fix

    I have reproduced this DoS vulnerability locally in a Linux environment.

    • Operating System: Ubuntu 22.04 LTS (x86_64)
    • Framework: NASA core Flight System (cFS) / cFE (Latest Release)
    • Target Platform: pc-linux
    • Toolchain: Native gcc (version 11.4.0)

    Mechanism:-

    Bypass: The pc-linux implementation of CFE_PSP_MemValidateRange() frequently returns CFE_SUCCESS for user space addresses because it lacks a strict hardware backed memory map.
    Trigger: Sending a CS_OneShotCmd with an unmapped address bypasses the initial PSP check.
    Result: The child task attempts to dereference the pointer via CFE_ES_CalculateCRC(), resulting in a SIGSEGV which terminates the entire application.

    My Analysis:

    The issue is a "Defense in Depth" failure. The current implementation of CS_OneShotCmd relies exclusively on CFE_PSP_MemValidateRange. On many platforms, this check is not strict enough to guarantee that an address is actually mapped or safe to read. This allows unmapped addresses to be passed into the CS_OneShotChildTask.

    When the child task calls CFE_ES_CalculateCRC, it attempts to dereference the invalid pointer, triggering a SIGSEGV and causing an abnormal termination of the CS application.

    I have am implementing a fix that would introduce a secondary, application level validation layer before any child task is spawned:

    1. Utility: Creats CS_VerifyAddressRange to validate requested memory ranges against the application's own configuration. This includes the cFE Core text segment, the OS text segment, and any entries already registered in the active Memory or EEPROM tables.
    2. OneShot Flow: Modifying CS_OneShotCmd in cs_cmds.c to perform this secondary check. If the requested range falls outside permitted operational regions, the command is rejected, a new event ID (CS_ONESHOT_MEMRANGE_ERR_EID) is issued, and the command error counter is incremented.
    3. Prevention: By catching the invalid address in the main task, we can prevent the creation of the child task and avoid the prohibited memory access entirely.

    I am finalizing the tests for this logic and will submit a Pull Request shortly @k-spacesec.

  2. added this to the v7.0.2 milestone on Mar 9, 2026
  3. keegan-moore commented on Apr 3, 2026

    @keegan-moore
    Contributor

    @dzbaker, @dmolock-NASA, & @jphickey - I'd like to get your thoughts on this. To me, it seems like this is more like a problem with our PSP, rather than an issue with CS. Perhaps we could restrict the valid address range better?

  4. Shrey-N commented on Apr 4, 2026

    @Shrey-N

    Hi @keegan-moore I believe you are correct this is a problem with the PSP. The pc-linux implementation of CFE_PSP_MemValidateRange is currently a stub that validates the entire address space (0 to SIZE_MAX) as RAM:

    /* fsw/pc-linux/src/cfe_psp_memory.c line 635 */
    CFE_PSP_MemRangeSet(0, CFE_PSP_MEM_RAM, 0, SIZE_MAX, CFE_PSP_MEM_SIZE_DWORD, CFE_PSP_MEM_ATTR_READWRITE);
    

    I believe fixing this in the PSP should protect all applications system wide :)

    I am happy to write a PR in PSP, asap!

  5. k-spacesec commented on Apr 7, 2026

    @k-spacesec
    Author

    Thanks @keegan-moore for the insight — I agree that this is fundamentally a PSP-level issue rather than a CS application bug. Building on @Shrey-N’s analysis, I’d like to provide a detailed root cause breakdown and explain why addressing this in the PSP is important, even for a development-only platform.


    1. Source-to-Sink Flow Analysis

    To clarify the vulnerability path:

    • Source (Attacker-Controlled Input):
      Address and Size fields in CS_OneShotCmd_t, received via the Software Bus.

    • Broken Validation (Ineffective Policy Enforcement):
      CS_OneShotCmd() forwards these values to CFE_PSP_MemValidateRange().
      On the pc-linux PSP, this always returns CFE_PSP_SUCCESS due to a permissive memory table (0 → SIZE_MAX), effectively bypassing validation.

    • Sink (Unsafe Memory Dereference):
      The unchecked address is passed to a child task invoking CFE_ES_CalculateCRC(), resulting in a dereference of attacker-controlled memory and an immediate SIGSEGV.

    Summary:
    Attacker-controlled input → ineffective PSP validation → unsafe memory dereference.

    The CS application correctly follows a validate-then-use pattern. However, the security failure arises because the PSP validation layer does not enforce any real constraints on the Linux platform.

    In scenarios where the supplied address is mapped, this may lead to unintended memory access, including potential disclosure or corruption.


    2. Root Cause: Inconsistent PSP Memory Table Initialization

    The issue originates from how different PSP platforms initialize the SysMemoryTable.

    PSP Platform Implementation File Memory Range Configuration Effective Coverage
    pc-linux fsw/pc-linux/src/cfe_psp_memory.c MemRangeSet(0, MEM_RAM, 0, SIZE_MAX, ...) Entire virtual address space
    mcp750-vxworks fsw/mcp750-vxworks/src/... MemRangeSet(0, MEM_RAM, 0, 0x8000000, ...) ~128MB (physical RAM)

    While the shared validation logic in cfe_psp_memrange.c is sound, the pc-linux implementation effectively nullifies the validation mechanism by using an overly permissive memory range.

    This is explicitly acknowledged in the source:

    "On Linux this just encompasses the entire memory space, but an entry needs to exist so that CFE_PSP_ValidateMemRange() works as intended."

    This design prioritizes functional compatibility, but removes any meaningful protection against invalid memory access.


    3. Why This Matters (Even for a Development PSP)

    Although pc-linux is not intended for flight, this behavior has several important implications:

    • Failure Mode Masking ("Test as You Fly" gap):
      Invalid memory accesses will consistently pass validation on pc-linux but fail on real hardware, masking critical memory safety issues during development.

    • Propagation of Insecure Defaults:
      The pc-linux PSP is often used as a reference for new platforms, increasing the risk that the 0 → SIZE_MAX pattern is unintentionally reused.

    • Reduced Simulation Fidelity:
      The lack of realistic memory validation undermines the reliability of integration and security testing in HITL/SITL environments.


    4. Suggested Approach: Platform-Native Memory Validation

    This can be treated as a hardening opportunity to align the pc-linux PSP with the intent of flight-ready implementations.

    Each platform’s CFE_PSP_SetupReservedMemoryMap() should populate the memory table using platform-native mechanisms:

    • Linux (pc-linux):
      Use /proc/self/maps or equivalent OS-provided mechanisms to identify mapped and accessible virtual address regions.

    • VxWorks:
      Use APIs such as vxMemProbe().

    • RTEMS / Bare-metal:
      Use BSP-provided linker symbols (e.g., _end, _stack).

    The goal is to enforce basic accessibility and boundary checks consistent with actual runtime behavior, rather than allowing all addresses by default.


    If this direction aligns with the project's goals, I'd be glad to contribute a PR for the pc-linux PSP. Please feel free to reach out if any further analysis or clarification would be helpful.

  6. added theissue type on May 14, 2026
  7. Shrey-N commented on Jul 1, 2026

    @Shrey-N

    Hi, just checking in on this issue. I submitted the proposed PSP level fix in PSP#469 a while back. Please let me know if there is any feedback or if you need me to make any adjustments to get this merged!

  8. ddstewar commented on Oct 27, 2026

    @ddstewar
    Contributor

    @dzbaker This has been confirmed by the reworking of the CS test script. This was found and then the search brought up this issue. I'll have @harun1e provide more details he found as well for this while developing the test.

  9. harun1e commented on Oct 27, 2026

    @harun1e

    Here are my findings while writing the CS test script.

    Any address other than 0x0000 in the one-shot command's argument causes a segmentation fault.

    The address 0x0000 works because in CFE_ES_CalculateCRC, if the DataPtr is NULL, it safely returns InputCRC (which is 0 in the first iteration). This is the case for all sizes up to the default MaxBytesPerCycle = (1024 * 16) or 0x4000 (the default is selected when the MaxBytesPerCycle parameter is 0). Any size greater than this limit will cause a segmentation fault because only MaxBytesPerCycle bytes are passed into the first cycle, and any remaining bytes are executed in subsequent cycles with an incremented address.

    For example, if size = 0x4008:

    • The first cycle gets address = 0x0000 and size = 0x4000.
    • The next cycle gets address = 0x4000 and size = 0x0008.

    Operating on the incremented, non-NULL address 0x4000 then triggers the segmentation fault.

    The same behavior applies to the MaxBytesPerCycle parameter itself. Size and MaxBytesPerCycle go hand in hand: as long as Size > MaxBytesPerCycle, a segmentation fault will occur.

    This issue is unique to the Linux platform due to user-space memory protection. The MM application exhibits the same behavior, where attempting to PEEK into an address like 0x0000 also causes a segmentation fault as expected.

    Everything else I've found is already covered in this Issue

  10. jphickey commented on Oct 30, 2026

    @jphickey
    Contributor

    I like the idea of offering an implementation of the CFE_PSP_MemValidateRange() for Linux that checks the address against /proc/$PID/maps

    This would also be beneficial for MD and any other app that works on memory addresses.

    The current implementation of CFE_PSP_MemValidateRange() in the PSP is nothing more than a placeholder. It does not do anything. Therefore the possibility of a segfault/bad address due to this is understood - the pc-linux PSP is an example and absolutely not a hardened implementation.

  11. Shrey-N commented on Oct 31, 2026

    @Shrey-N

    @jphickey I have already implemented this design a couple of months ago in nasa/PSP#469, it parses /proc/self/maps at startup to populate the pc-linux memory validation table.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions