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.
Current Behavior
serializeDataflashReadReply()truncates a DATAFLASH read against the end of the FlashFS volume using:There is no check that
address <= flashfsSizebefore the subtraction.If
addressis greater than the FlashFS size,flashfsSize - addressunderflows as an unsigned value.The downstream
flashfsReadAbs()function performs a similar subtraction:flashReadBytes()passes the length to the selected flash driver'sreadBytes()callback as anint.On the W25N backend,
w25n_readBytes()stores the selected transfer size in auint16_t:For the modeled case
flashfsSize = 1024andaddress = 1025, the length eventually becomes-1as anint, and then65535when assigned to the W25Nuint16_t transferLength.Affected source in the tested commit:
https://github.com/iNavFlight/inav/blob/c5c593d71d33c8e284bf9cd34381588fda7a98c8/src/main/fc/fc_msp.c#L2793-L2822https://github.com/iNavFlight/inav/blob/c5c593d71d33c8e284bf9cd34381588fda7a98c8/src/main/io/flashfs.c#L2107-L2128https://github.com/iNavFlight/inav/blob/c5c593d71d33c8e284bf9cd34381588fda7a98c8/src/main/drivers/flash.c#L1028-L1033https://github.com/iNavFlight/inav/blob/c5c593d71d33c8e284bf9cd34381588fda7a98c8/src/main/drivers/flash_w25n.c#L2131-L2175Security impact
This is security-relevant because an MSP-capable peer can supply the
addressused byMSP_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:
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
What is not proven
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:Compile and run:
Observed locally:
The tested INAV tree was:
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:
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.