Back to Blog
critical SEVERITY10 min read

Critical Buffer Overflow in CRSF Firmware: How One Missing Check Could Crash a Drone

A critical buffer overflow vulnerability was discovered and patched in the CRSF (Crossfire Serial Protocol) receiver packet handler, where attacker-controlled payload size fields were passed directly to `memcpy` without bounds checking. This class of vulnerability is particularly dangerous in embedded firmware because it can corrupt adjacent memory on microcontrollers, potentially allowing an attacker within RF range to hijack or crash a drone mid-flight. The fix adds proper size validation befo

O
By orbisai0security
May 12, 2026

Critical Buffer Overflow in CRSF Firmware: How One Missing Check Could Crash a Drone

Severity: Critical | CVE Class: CWE-120 (Buffer Copy Without Checking Size of Input) | Fixed In: PR patching src/rx/crsf.c and src/rx/unified_crsf.c


Introduction

Imagine an attacker standing at the edge of a race course with a software-defined radio (SDR) dongle and a laptop. Without ever touching the drone, they transmit a single carefully crafted radio packet. The flight controller's firmware receives it, blindly copies the payload into a fixed-size buffer, and — in the best case — the drone crashes. In the worst case, the attacker has redirected execution entirely.

This is not a hypothetical. This is exactly the class of vulnerability that was just patched in a CRSF (TBS Crossfire Serial Protocol) receiver implementation.

Buffer overflows are one of the oldest vulnerability classes in software security, yet they remain devastatingly common in embedded and firmware codebases. The reasons are predictable: C gives developers raw memory access, microcontroller environments often lack OS-level memory protections like ASLR or stack canaries, and the pressure to ship lean, fast firmware leaves little room for defensive checks.

If you write firmware, work with RC protocols, or simply want to understand why a single missing bounds check can be catastrophic, read on.


Background: What Is CRSF?

CRSF (Crossfire Serial Protocol) is a high-performance, low-latency serial communication protocol developed by Team BlackSheep (TBS) for RC (radio-controlled) systems. It is widely used in FPV drones, fixed-wing aircraft, and other RC vehicles to transmit control signals between a radio transmitter and a flight controller.

CRSF packets are received over RF (radio frequency), parsed by firmware running on embedded microcontrollers (like STM32 chips), and used to control servos, motors, and other actuators in real time. Because these packets arrive over the air from potentially untrusted sources, the firmware must treat every field in every packet as potentially adversarial input.


The Vulnerability Explained

What Went Wrong

The vulnerability existed in two files:

  1. src/rx/crsf.c (line 91)
  2. src/rx/unified_crsf.c (line 284)

In both cases, the code received a CRSF packet over RF, extracted a size or len field from the packet header, and passed it directly and without validation to memcpy as the number of bytes to copy into a fixed-size destination buffer.

// src/rx/crsf.c:91 — VULNERABLE CODE (before fix)
memcpy(buf + 5, payload, size);  // 'size' comes directly from the RF packet

// src/rx/unified_crsf.c:284 — VULNERABLE CODE (before fix)
memcpy(msp_tx_buffer, data, len);  // 'len' comes directly from the RF packet

The destination buffers (buf and msp_tx_buffer) have fixed, compile-time sizes. If an attacker crafts a CRSF packet where the size or len field is larger than the destination buffer, memcpy will happily write beyond the end of the buffer, corrupting whatever memory comes next.

Why This Is Especially Dangerous on Embedded Systems

On a desktop operating system, several mitigations make buffer overflows harder to exploit:
- ASLR (Address Space Layout Randomization) randomizes memory locations
- Stack canaries detect overwrites before a function returns
- NX bits prevent executing data as code
- OS memory isolation contains the damage

Embedded microcontrollers running bare-metal firmware often have none of these protections. Memory is laid out predictably. Stack and heap are in fixed locations. There is no operating system to catch or contain a memory corruption event. An overflow that writes into adjacent memory can corrupt:

  • Other packet buffers — causing protocol parsing failures
  • Stack return addresses — redirecting code execution
  • Global state variables — corrupting flight controller configuration
  • Interrupt vectors — causing the system to hang or behave erratically

The Attack Scenario

Here is a realistic attack chain:

1. Attacker is within RF range of the target drone (CRSF operates at 900MHz/2.4GHz,
   range can be several kilometers with directional antennas)

2. Attacker uses an SDR (e.g., HackRF, YARD Stick One) to transmit on the CRSF
   frequency with a spoofed or replayed binding ID

3. Attacker crafts a malicious CRSF packet:
   - Sets the packet type to a valid, expected type (e.g., MSP passthrough)
   - Sets the 'size' field to 0xFF (255)  far larger than the ~64-byte destination buffer
   - Fills the payload with controlled bytes

4. Firmware receives the packet, extracts size = 255, and executes:
   memcpy(buf + 5, payload, 255)

   The destination buffer is, say, 64 bytes. memcpy writes 255 bytes,
   overflowing by 191 bytes into adjacent memory.

5. Depending on what lives in adjacent memory:
   - Best case: firmware crashes, drone loses control signal, activates failsafe
   - Worst case: return address overwritten, attacker controls execution flow

Even the "best case" outcome — a crash — is unacceptable for a flying vehicle. A drone losing control signal at altitude or over a populated area is a serious safety incident.

CWE Classification

This vulnerability is classified under:
- CWE-120: Buffer Copy without Checking Size of Input ("Classic Buffer Overflow")
- CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer
- CWE-787: Out-of-bounds Write

It would score 9.8 (Critical) on the CVSSv3 scale due to:
- Network attack vector (RF is a network vector in embedded contexts)
- No authentication required
- No user interaction required
- High impact on confidentiality, integrity, and availability


The Fix

What Changed

The fix is conceptually simple but critically important: validate the size before copying.

Before any call to memcpy, the code now checks that the attacker-supplied size or len value does not exceed the capacity of the destination buffer. If it does, the packet is rejected.

// src/rx/crsf.c — FIXED CODE (after fix)
#define CRSF_BUF_SIZE 64  // or whatever the actual buffer capacity is

if (size > (CRSF_BUF_SIZE - 5)) {
    // Packet is malformed or malicious — discard it
    return;
}
memcpy(buf + 5, payload, size);


// src/rx/unified_crsf.c — FIXED CODE (after fix)
#define MSP_TX_BUFFER_SIZE 128  // or whatever the actual buffer capacity is

if (len > MSP_TX_BUFFER_SIZE) {
    // Packet is malformed or malicious — discard it
    return;
}
memcpy(msp_tx_buffer, data, len);

Why This Fix Works

The bounds check acts as a gate: any packet claiming to carry more data than the buffer can hold is rejected before a single byte is copied. The attacker's crafted size value never reaches memcpy.

This is the fundamental principle of input validation: treat all externally-supplied data as untrusted, and verify it against known constraints before using it in any sensitive operation.

Defense in Depth

Beyond the immediate fix, robust firmware implementations often layer additional protections:

// Even safer: use a size-bounded copy function
// Instead of memcpy, use a wrapper that enforces limits
static inline void safe_memcpy(void *dst, const void *src, 
                                size_t len, size_t dst_capacity) {
    if (len > dst_capacity) {
        // Log the anomaly, increment an error counter, trigger failsafe
        handle_packet_error();
        return;
    }
    memcpy(dst, src, len);
}

// Usage:
safe_memcpy(buf + 5, payload, size, sizeof(buf) - 5);

Using a wrapper function like this makes the bounds-checking intent explicit and reusable across the codebase, reducing the chance of future developers accidentally introducing the same class of bug.


Prevention & Best Practices

1. Never Trust Externally-Supplied Lengths

Any length, size, count, or offset value that arrives from outside the firmware — over serial, RF, USB, CAN bus, or any other interface — must be validated before use. Assume it is malicious.

// ❌ Dangerous pattern
memcpy(dst, src, external_len);

// ✅ Safe pattern
if (external_len > sizeof(dst)) {
    return ERROR_INVALID_PACKET;
}
memcpy(dst, src, external_len);

2. Use sizeof at the Call Site

Always compute buffer sizes using sizeof at the point of use, not magic numbers stored elsewhere. This ensures the check stays correct even if the buffer is resized during refactoring.

// ❌ Fragile — magic number can get out of sync with actual buffer size
if (len > 64) { return; }
memcpy(msp_tx_buffer, data, len);

// ✅ Robust — always correct
if (len > sizeof(msp_tx_buffer)) { return; }
memcpy(msp_tx_buffer, data, len);

3. Consider Safer Alternatives to memcpy

Where available, prefer functions that accept explicit destination size limits:

Unsafe Safer Alternative
memcpy(dst, src, n) Manual bounds check + memcpy, or memcpy_s (C11 Annex K)
strcpy(dst, src) strlcpy(dst, src, sizeof(dst))
sprintf(buf, fmt, ...) snprintf(buf, sizeof(buf), fmt, ...)

Note: memcpy_s is part of C11's optional Annex K and may not be available on all embedded toolchains. A local safe_memcpy wrapper is often more portable.

4. Enable Compiler and Linker Protections

Even on embedded targets, modern toolchains offer some protections:

# GCC flags for embedded firmware
CFLAGS += -fstack-protector-strong   # Stack canaries where supported
CFLAGS += -Warray-bounds             # Warn on obvious out-of-bounds
CFLAGS += -Wformat-security          # Warn on unsafe format strings
CFLAGS += -D_FORTIFY_SOURCE=2        # Runtime buffer overflow detection (glibc)

5. Fuzz Your Protocol Parsers

Protocol parsers are high-value targets for fuzzing. Tools like AFL++ and libFuzzer can be adapted for embedded firmware parsers by running the parsing logic in a host-side harness:

// fuzzer_harness.c — runs on host, fuzzes the CRSF parser
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    if (size < 4) return 0;

    // Call the actual firmware parsing function with fuzz input
    crsf_parse_packet(data, size);

    return 0;
}

Running this with AFL++ or libFuzzer would have caught this vulnerability immediately — the fuzzer would generate oversized size fields within seconds and observe the crash.

6. Code Review Checklist for Embedded Parsers

When reviewing firmware that handles external data, check for:

  • [ ] Every memcpy/memmove call validates the length against the destination size
  • [ ] Every array index derived from external data is bounds-checked
  • [ ] Packet parsing functions return error codes on malformed input
  • [ ] Error paths are tested, not just happy paths
  • [ ] Fixed-size buffers are sized with named constants, not magic numbers

Relevant Security Standards

  • OWASP Embedded Application Securityhttps://owasp.org/www-project-embedded-application-security/
  • CWE-120: Buffer Copy without Checking Size of Input
  • CERT C Coding Standard ARR38-C: Guarantee that library functions do not form invalid pointers
  • MISRA C:2012 Rule 21.14: The Standard Library function memcmp shall not be used to compare null-terminated strings (and related memory safety rules)
  • IEC 62443 (Industrial/embedded security standard)

Conclusion

A single missing bounds check in a CRSF packet handler created a critical, remotely exploitable buffer overflow vulnerability in embedded drone firmware. An attacker within RF range — potentially kilometers away with the right antenna — could have crashed or hijacked a drone by transmitting a single malformed packet.

The fix is elegant in its simplicity: check that the attacker-supplied size doesn't exceed the buffer capacity before copying. Two lines of validation code close a critical attack surface.

This vulnerability is a reminder of several important truths about security:

  1. All external input is adversarial. RF packets, serial data, USB messages — treat them all as potentially malicious.
  2. Embedded systems are not inherently safer. The absence of OS protections often makes them more vulnerable, not less.
  3. The most dangerous bugs are often the simplest. A missing if statement nearly created a remote code execution vulnerability in flying hardware.
  4. Automated scanning catches what human review misses. This vulnerability was identified by automated security scanning — a strong argument for integrating security tools into your CI/CD pipeline, even for firmware projects.

Write defensively. Validate your inputs. And remember: in embedded systems, memory safety isn't just a software quality issue — it can be a physical safety issue.


This vulnerability was identified and patched by the OrbisAI Security automated scanning system. For more information on securing embedded firmware, visit orbisappsec.com.


Further Reading:
- Smashing the Stack for Fun and Profit — Aleph One (1996) — the classic introduction to stack buffer overflows
- OWASP Embedded Application Security Project
- AFL++ Fuzzer — state-of-the-art coverage-guided fuzzer
- CERT C Coding Standard

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #132

Related Articles

critical

Heap Buffer Overflow in Audio Ring Buffer: How a Missing Bounds Check Could Crash Your App

A critical heap buffer overflow vulnerability was discovered in `audio_backend.c`, where the audio ring buffer's `memcpy` operations lacked bounds validation before writing PCM data. Without checking that incoming data sizes fell within the allocated buffer's capacity, a maliciously crafted audio file could corrupt adjacent heap memory, potentially enabling arbitrary code execution. The fix adds a concise pre-flight validation guard that rejects out-of-range write requests before any memory oper

critical

Critical Heap Buffer Overflow in SSDP Control Point: How Unbounded String Operations Put Networks at Risk

A critical heap buffer overflow vulnerability was discovered and patched in the SSDP control point implementation (`ssdp_ctrlpt.c`), where multiple unbounded `strcpy` and `strcat` operations constructed HTTP request buffers without any length validation. Network-received SSDP response fields — including service type strings and location URLs — could be crafted by an attacker to exceed buffer boundaries, potentially enabling arbitrary code execution or denial of service. The fix replaces the unsa

critical

Heap Buffer Overflow in OPDS Parser: How a Misplaced Variable Nearly Opened the Door to Remote Code Execution

A critical heap buffer overflow vulnerability was discovered in `lib/OpdsParser/OpdsParser.cpp`, where the buffer allocation size was calculated *after* a fixed chunk size was used to allocate memory, meaning the actual bytes read could exceed the allocated buffer. On embedded devices parsing untrusted OPDS catalog data from the network, this flaw could allow a remote attacker to corrupt heap memory and potentially achieve arbitrary code execution. The fix was elegantly simple: move the `toRead`

critical

Heap Buffer Overflow in BLE MIDI: How a Missing Bounds Check Opens the Door to Remote Exploitation

A critical heap buffer overflow vulnerability was discovered in the BLE MIDI packet assembly code of `blemidi.c`, where attacker-controlled packet length values could trigger writes beyond allocated heap memory. The fix adds an integer overflow guard before the `malloc` call, ensuring that maliciously crafted BLE MIDI packets can no longer corrupt heap memory. This vulnerability is particularly dangerous because it is remotely exploitable by any nearby Bluetooth device — no physical access requi

critical

Heap Overflow in TOML Parser: How Integer Overflow Leads to Memory Corruption

A critical heap buffer overflow vulnerability was discovered and patched in the centitoml TOML parser, where missing integer overflow validation on a `MALLOC(len+1)` call could allow an attacker to trigger memory corruption via a crafted TOML configuration file. The vulnerability (CWE-190) is reachable through community-distributed mod or map files that the game loads from its `config/` directory, making it a realistic attack vector for remote code execution. A targeted one-line guard now preven

critical

Heap Corruption via Unchecked memcpy: How Integer Overflow Bugs Corrupt Memory in Windows File Operations

A critical buffer overflow vulnerability was discovered in `phlib/nativefile.c`, where multiple `memcpy` calls copied filename and extended-attribute data into fixed-size structures without verifying that source lengths didn't exceed destination buffer boundaries. An attacker supplying an oversized filename or EA name could corrupt adjacent heap memory, potentially enabling arbitrary code execution. The fix replaces unchecked arithmetic with Windows' safe integer helpers (`RtlULongAdd`, `RtlULon