Current Behavior
The MSP passthrough handler treats a zero-length request as the legacy ESC 4-way mode:
const unsigned int dataSize = sbufBytesRemaining(src);
if (dataSize == 0) {
mspPassthroughMode = MSP_PASSTHROUGH_ESC_4WAY;
}
For MSP_PASSTHROUGH_ESC_4WAY, the handler immediately calls:
sbufWriteU8(dst, esc4wayInit());
No armed-state check is performed in this path.
esc4wayInit() begins by disabling the motor PWM outputs:
uint8_t esc4wayInit(void)
{
pwmDisableMotors();
...
}
Affected source in the tested commit:
https://github.com/iNavFlight/inav/blob/c5c593d71d33c8e284bf9cd34381588fda7a98c8/src/main/fc/fc_msp.c#L2547-L2610
https://github.com/iNavFlight/inav/blob/c5c593d71d33c8e284bf9cd34381588fda7a98c8/src/main/io/serial_4way.c#L2193-L2224
MSP-over-telemetry commands are passed into the normal FC MSP dispatcher:
https://github.com/iNavFlight/inav/blob/c5c593d71d33c8e284bf9cd34381588fda7a98c8/src/main/telemetry/msp_shared.c#L840-L865
Security impact
This is primarily a safety-impacting denial-of-service / control-plane vulnerability.
An accepted MSP passthrough command can reach esc4wayInit() without first checking whether the flight controller is armed. esc4wayInit() calls pwmDisableMotors().
If an attacker or compromised MSP-capable peer can issue this command while the aircraft is armed, the command path can disable motor outputs during an armed state. On a flying multirotor, that can cause immediate loss of propulsion and loss of the aircraft.
This is security-relevant even if the MSP peer is normally considered trusted: a security boundary exists wherever an external peer can issue protocol commands that affect flight-critical outputs, and the command lacks the state guard used by other dangerous operations.
Attacker preconditions
The attacker or peer must be able to submit accepted MSP commands through the configured MSP transport. This report does not claim that an arbitrary nearby radio user can do so on every INAV installation.
What is proven
- The zero-length
MSP_SET_PASSTHROUGH path reaches ESC 4-way initialization.
- The production
esc4wayInit() implementation calls pwmDisableMotors().
- No armed-state check is present before that call in the tested path.
- A host-side control-flow harness reproduces the armed → motor-disabled state transition.
What is not proven
- An in-flight test.
- A physical FC test with motors connected.
- The exact behavior of every supported target/ESC configuration.
Steps to Reproduce
I verified the production call chain in INAV 9.1 source and reproduced the missing state gate in a small host-side control-flow harness.
This is not a test performed with motors connected, a flying aircraft, or a physical FC.
Save as poc_inav_armed_passthrough.c:
#include <stdbool.h>
#include <stdio.h>
static bool armed = true;
static bool motors_disabled = false;
static unsigned esc4wayInit(void)
{
motors_disabled = true; /* models pwmDisableMotors() */
return 4;
}
static void msp_set_passthrough(unsigned payload_size)
{
enum { MSP_PASSTHROUGH_ESC_4WAY = 0 } mode;
if (payload_size == 0) {
mode = MSP_PASSTHROUGH_ESC_4WAY;
} else {
return;
}
if (mode == MSP_PASSTHROUGH_ESC_4WAY) {
(void)esc4wayInit();
}
}
int main(void)
{
printf("armed before=%s motors_disabled before=%s\n",
armed ? "true" : "false",
motors_disabled ? "true" : "false");
msp_set_passthrough(0);
printf("armed after=%s motors_disabled after=%s\n",
armed ? "true" : "false",
motors_disabled ? "true" : "false");
if (armed && motors_disabled) {
puts("REPRODUCED: passthrough reached motor-disable path while armed");
return 0;
}
return 1;
}
Compile and run:
gcc -O0 -g poc_inav_armed_passthrough.c -o poc_inav_armed_passthrough
./poc_inav_armed_passthrough
Observed locally:
armed before=true motors_disabled before=false
armed after=true motors_disabled after=true
REPRODUCED: passthrough reached motor-disable path while armed
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
Entering ESC programming/passthrough mode should be rejected while the flight controller is armed.
A command that disables or reconfigures motor outputs should require an explicit safe/disarmed state before any motor-output state is modified.
Suggested solution(s)
Reject ESC 4-way passthrough when armed before calling esc4wayInit(), for example at the MSP command-handler level.
The state check should occur before pwmDisableMotors() or any GPIO reconfiguration.
Additional context
The safety impact is the main concern: the production call chain allows the ESC 4-way initialization routine, which disables motor outputs, to be reached without an armed-state check.
The security reachability depends on whether a peer can send accepted MSP commands through the configured transport. INAV's shared MSP-over-telemetry path calls the normal MSP command dispatcher, but individual radio/telemetry links may impose their own trust or authentication boundary.
Current Behavior
The MSP passthrough handler treats a zero-length request as the legacy ESC 4-way mode:
For
MSP_PASSTHROUGH_ESC_4WAY, the handler immediately calls:No armed-state check is performed in this path.
esc4wayInit()begins by disabling the motor PWM outputs:Affected source in the tested commit:
https://github.com/iNavFlight/inav/blob/c5c593d71d33c8e284bf9cd34381588fda7a98c8/src/main/fc/fc_msp.c#L2547-L2610https://github.com/iNavFlight/inav/blob/c5c593d71d33c8e284bf9cd34381588fda7a98c8/src/main/io/serial_4way.c#L2193-L2224MSP-over-telemetry commands are passed into the normal FC MSP dispatcher:
https://github.com/iNavFlight/inav/blob/c5c593d71d33c8e284bf9cd34381588fda7a98c8/src/main/telemetry/msp_shared.c#L840-L865Security impact
This is primarily a safety-impacting denial-of-service / control-plane vulnerability.
An accepted MSP passthrough command can reach
esc4wayInit()without first checking whether the flight controller is armed.esc4wayInit()callspwmDisableMotors().If an attacker or compromised MSP-capable peer can issue this command while the aircraft is armed, the command path can disable motor outputs during an armed state. On a flying multirotor, that can cause immediate loss of propulsion and loss of the aircraft.
This is security-relevant even if the MSP peer is normally considered trusted: a security boundary exists wherever an external peer can issue protocol commands that affect flight-critical outputs, and the command lacks the state guard used by other dangerous operations.
Attacker preconditions
The attacker or peer must be able to submit accepted MSP commands through the configured MSP transport. This report does not claim that an arbitrary nearby radio user can do so on every INAV installation.
What is proven
MSP_SET_PASSTHROUGHpath reaches ESC 4-way initialization.esc4wayInit()implementation callspwmDisableMotors().What is not proven
Steps to Reproduce
I verified the production call chain in INAV 9.1 source and reproduced the missing state gate in a small host-side control-flow harness.
This is not a test performed with motors connected, a flying aircraft, or a physical FC.
Save as
poc_inav_armed_passthrough.c:Compile and run:
Observed locally:
The tested INAV tree was:
Expected behavior
Entering ESC programming/passthrough mode should be rejected while the flight controller is armed.
A command that disables or reconfigures motor outputs should require an explicit safe/disarmed state before any motor-output state is modified.
Suggested solution(s)
Reject ESC 4-way passthrough when armed before calling
esc4wayInit(), for example at the MSP command-handler level.The state check should occur before
pwmDisableMotors()or any GPIO reconfiguration.Additional context
The safety impact is the main concern: the production call chain allows the ESC 4-way initialization routine, which disables motor outputs, to be reached without an armed-state check.
The security reachability depends on whether a peer can send accepted MSP commands through the configured transport. INAV's shared MSP-over-telemetry path calls the normal MSP command dispatcher, but individual radio/telemetry links may impose their own trust or authentication boundary.