Skip to content

MSP_DATAFLASH_READ out-of-range address can underflow length and cause an oversized W25N read #11778

Description

@jFriedli

Current Behavior

serializeDataflashReadReply() truncates a DATAFLASH read against the end of the FlashFS volume using:

const uint32_t flashfsSize = flashfsGetSize();
if (readLen > flashfsSize - address) {
    readLen = flashfsSize - address;
}

There is no check that address <= flashfsSize before the subtraction.

If address is greater than the FlashFS size, flashfsSize - address underflows as an unsigned value.

The downstream flashfsReadAbs() function performs a similar subtraction:

if (address + len > flashfsGetSize()) {
    len = flashfsGetSize() - address;
}

bytesRead = flashReadBytes(address, buffer, len);

flashReadBytes() passes the length to the selected flash driver's readBytes() callback as an int.

On the W25N backend, w25n_readBytes() stores the selected transfer size in a uint16_t:

uint16_t transferLength;

if (length > W25N_PAGE_SIZE - column) {
    transferLength = W25N_PAGE_SIZE - column;
} else {
    transferLength = length;
}

For the modeled case flashfsSize = 1024 and address = 1025, the length eventually becomes -1 as an int, and then 65535 when assigned to the W25N uint16_t transferLength.

Affected source in the tested commit:

  • https://github.com/iNavFlight/inav/blob/c5c593d71d33c8e284bf9cd34381588fda7a98c8/src/main/fc/fc_msp.c#L2793-L2822
  • https://github.com/iNavFlight/inav/blob/c5c593d71d33c8e284bf9cd34381588fda7a98c8/src/main/io/flashfs.c#L2107-L2128
  • https://github.com/iNavFlight/inav/blob/c5c593d71d33c8e284bf9cd34381588fda7a98c8/src/main/drivers/flash.c#L1028-L1033
  • https://github.com/iNavFlight/inav/blob/c5c593d71d33c8e284bf9cd34381588fda7a98c8/src/main/drivers/flash_w25n.c#L2131-L2175

Security impact

This is security-relevant because an MSP-capable peer can supply the address used by MSP_DATAFLASH_READ. If that address is outside the FlashFS range, the arithmetic can underflow and reach the flash backend as an oversized transfer.

On the W25N path modeled here, the invalid length becomes a 65,535-byte transfer into a response buffer that is much smaller. That creates a memory-corruption condition inside the flight-controller process.

Potential consequences include:

  • flight-controller crash or reset,
  • corruption of adjacent runtime state,
  • loss of flight control / denial of service,
  • and, depending on memory layout and target architecture, potentially more severe memory-corruption effects.

I have not demonstrated controlled code execution or a real FC crash, so this report does not claim RCE.

Attacker preconditions

The attacker or peer must be able to submit an accepted MSP command to the target. This can be a local MSP connection or, depending on deployment, an MSP-capable telemetry/RC bridge. This report does not assume that every INAV installation exposes MSP to arbitrary nearby radio users.

What is proven

  • The vulnerable arithmetic is present in the tested INAV 9.1 source.
  • The downstream W25N signed/unsigned conversion is present in production source.
  • A host-side sanitizer harness reproducing that production data flow produces a 65,535-byte write into a 512-byte buffer.

What is not proven

  • A crash on a physical flight controller.
  • Remote reachability on every INAV deployment.
  • Reliable code execution.

Steps to Reproduce

I reproduced the arithmetic and W25N length conversion in a host-side AddressSanitizer harness.

This is not a crash captured from a physical INAV flight controller. The harness models the affected production arithmetic and the downstream W25N transfer-size conversion.

Save as poc_inav_dataflash_address_underflow.c:

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static const uint32_t flash_size = 1024;

static int modeled_flashfs_read_abs(uint32_t address, uint8_t *buffer, unsigned int len)
{
    if (address + len > flash_size) {
        len = flash_size - address;
    }

    int driver_len = (int)len;
    uint16_t w25n_transfer = (uint16_t)driver_len;

    printf("flashfs len=0x%08x, driver_len=%d, W25N transfer=%u\n",
           len, driver_len, w25n_transfer);
    fflush(stdout);

    memset(buffer, 0x41, w25n_transfer);
    return (int)w25n_transfer;
}

int main(void)
{
    const uint32_t address = flash_size + 1;
    uint16_t read_len = 128;
    uint8_t *response = malloc(512);
    if (!response) {
        return 2;
    }

    if (read_len > flash_size - address) {
        read_len = (uint16_t)(flash_size - address);
    }

    printf("flash_size=%u address=%u size-address=0x%08x read_len=%u\n",
           flash_size, address, flash_size - address, read_len);
    fflush(stdout);

    modeled_flashfs_read_abs(address, response, read_len);
    free(response);
    return 0;
}

Compile and run:

gcc -O0 -g -fsanitize=address,undefined     poc_inav_dataflash_address_underflow.c     -o poc_inav_dataflash_address_underflow

ASAN_OPTIONS=abort_on_error=1 ./poc_inav_dataflash_address_underflow

Observed locally:

flash_size=1024 address=1025 size-address=0xffffffff read_len=128
flashfs len=0xffffffff, driver_len=-1, W25N transfer=65535

ERROR: AddressSanitizer: heap-buffer-overflow
WRITE of size 65535

The tested INAV tree was:

commit: c5c593d71d33c8e284bf9cd34381588fda7a98c8
date:   2026-07-18 22:18:56 -0500
subject: Merge pull request #11728 from iNavFlight/release/9.1

Expected behavior

A DATAFLASH read whose start address is outside the FlashFS volume should be rejected or return an empty/error response.

No unsigned subtraction should be performed until the address has first been validated against the FlashFS size.

Suggested solution(s)

Validate the address before calculating any remaining length, for example:

if (address >= flashfsSize) {
    readLen = 0;
} else if (readLen > flashfsSize - address) {
    readLen = flashfsSize - address;
}

The same defensive check should be applied in flashfsReadAbs() so callers cannot trigger the underflow independently.

Additional context

The security impact depends on how MSP is exposed in a particular installation. This report does not claim that an arbitrary nearby radio attacker can necessarily reach the command.

The relevant condition is that an input source capable of sending accepted MSP commands can supply an out-of-range DATAFLASH address. INAV supports MSP through multiple transports, including telemetry integrations, but the trust and authentication properties of those transports depend on the deployment.

The host-side harness demonstrates the unsafe length conversion and resulting oversized write model. I have not reproduced the issue on a physical FC or demonstrated code execution.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions