Skip to content

SmartPort MSP response can transmit uninitialized stack bytes in a short final fragment #11780

Description

@jFriedli

Current Behavior

When MSP-over-SmartPort sends a response, smartPortSendMspResponse() creates a local smartPortPayload_t without initializing it:

static void smartPortSendMspResponse(uint8_t *data, const uint8_t dataSize)
{
    smartPortPayload_t payload;

    payload.frameId = FSSP_MSPS_FRAME;
    memcpy(&payload.valueId, data,
        MIN(dataSize, SMARTPORT_MSP_PAYLOAD_SIZE));

    smartPortWriteFrame(&payload);
}

Only dataSize bytes are copied into the six-byte MSP portion of the structure.

smartPortWriteFrameSerial() then serializes the complete smartPortPayload_t:

uint8_t *data = (uint8_t *)payload;

for (unsigned i = 0; i < sizeof(smartPortPayload_t); i++) {
    smartPortSendByte(*data++, &checksum, port);
}

Therefore, when the final MSP response fragment contains fewer than SMARTPORT_MSP_PAYLOAD_SIZE bytes, the unused tail of the local stack object is transmitted without having been initialized by this function.

Affected source in the tested commit:

  • https://github.com/iNavFlight/inav/blob/c5c593d71d33c8e284bf9cd34381588fda7a98c8/src/main/telemetry/smartport.c#L2104-L2118
  • https://github.com/iNavFlight/inav/blob/c5c593d71d33c8e284bf9cd34381588fda7a98c8/src/main/telemetry/smartport.c#L2230-L2242
  • https://github.com/iNavFlight/inav/blob/c5c593d71d33c8e284bf9cd34381588fda7a98c8/src/main/telemetry/smartport.h#L336-L337
  • https://github.com/iNavFlight/inav/blob/c5c593d71d33c8e284bf9cd34381588fda7a98c8/src/main/telemetry/smartport.h#L372-L378

Security impact

This is an information-disclosure vulnerability across the flight-controller to SmartPort telemetry boundary.

When the final MSP response fragment is shorter than the fixed SmartPort MSP payload, the unused bytes of the stack-allocated smartPortPayload_t are not initialized before the complete structure is serialized.

A telemetry peer receiving those frames can therefore receive bytes originating from previous stack contents.

The exact contents depend on compiler, target and runtime stack reuse. I have not demonstrated leakage of credentials, keys or a specific secret, so the report should not claim disclosure of any particular sensitive value.

Attacker preconditions

The observing party must be the SmartPort telemetry peer (or otherwise be able to observe that telemetry link). This is not a generic Internet-facing information leak.

What is proven

  • The short-copy/full-serialization pattern is present in the tested INAV source.
  • MemorySanitizer confirms that the modeled transmit path consumes uninitialized bytes.

What is not proven

  • Leakage of a specific secret from physical hardware.
  • Exposure to peers that cannot observe the SmartPort telemetry channel.

Steps to Reproduce

I reproduced the initialization pattern with Clang MemorySanitizer.

This is a host-side sanitizer reproduction of the production initialization/transmit pattern, not a capture from a physical SmartPort bus.

Save as poc_inav_smartport_uninitialized.c:

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

#define SMARTPORT_MSP_PAYLOAD_SIZE 6
#define FSSP_MSPS_FRAME 0x32
#define MIN(a,b) ((a) < (b) ? (a) : (b))

typedef struct __attribute__((packed)) {
    uint8_t frameId;
    uint16_t valueId;
    uint32_t data;
} smartPortPayload_t;

static uint8_t consume_all_bytes(const smartPortPayload_t *p)
{
    const uint8_t *b = (const uint8_t *)p;
    uint8_t x = 0;

    for (unsigned i = 0; i < sizeof(*p); i++) {
        x ^= b[i];
    }

    return x;
}

static uint8_t vulnerable_send(uint8_t *data, uint8_t dataSize)
{
    smartPortPayload_t payload;

    payload.frameId = FSSP_MSPS_FRAME;
    memcpy(&payload.valueId, data,
           MIN(dataSize, SMARTPORT_MSP_PAYLOAD_SIZE));

    return consume_all_bytes(&payload);
}

int main(void)
{
    uint8_t one = 0x41;
    volatile uint8_t sink = vulnerable_send(&one, 1);

    printf("sink=%u\n", sink);
    return 0;
}

Compile and run with Clang:

clang -O1 -g     -fsanitize=memory     -fno-omit-frame-pointer     poc_inav_smartport_uninitialized.c     -o poc_inav_smartport_uninitialized

./poc_inav_smartport_uninitialized

Observed locally:

WARNING: MemorySanitizer: use-of-uninitialized-value

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

All bytes of a telemetry frame should be initialized before the structure is serialized.

Short final MSP response fragments should contain deterministic padding rather than previous stack contents.

Suggested solution(s)

Zero-initialize the structure before filling it, for example:

smartPortPayload_t payload = {0};

payload.frameId = FSSP_MSPS_FRAME;
memcpy(&payload.valueId, data,
       MIN(dataSize, SMARTPORT_MSP_PAYLOAD_SIZE));

Alternatively, explicitly zero the unused payload tail.

Additional context

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