Back to Blog
high SEVERITY7 min read

Buffer Overflow in Meshtastic: How One Missing Bounds Check Opens the Door to Remote Code Execution

A critical buffer overflow vulnerability was discovered in the Meshtastic firmware's radio packet handler, where an unchecked `memcpy` operation allowed any node on the mesh network to send a crafted packet with an oversized payload length field, potentially overwriting adjacent memory. Because Meshtastic mesh nodes communicate without authentication, this vulnerability was remotely exploitable by any attacker within radio range — or even further through mesh relay. The fix adds a simple but ess

O
By orbisai0security
May 28, 2026

Buffer Overflow in Meshtastic: How One Missing Bounds Check Opens the Door to Remote Code Execution

Introduction

Mesh radio networks like Meshtastic are increasingly popular for off-grid, decentralized communication — used by hikers, emergency responders, preppers, and hobbyists worldwide. They're designed to work without internet infrastructure, relaying messages node-to-node over LoRa radio. That decentralized, open nature is a feature. But it also means any device within radio range can send packets to your node — including malicious ones.

This post breaks down a critical buffer overflow vulnerability discovered in meshtastic.cpp, explains how it could be exploited over the air, and walks through the fix. Whether you're an embedded developer, a security researcher, or just a Meshtastic user, this vulnerability is a reminder that trust boundaries matter even in hobbyist firmware.


The Vulnerability Explained

What Is a Buffer Overflow?

A buffer overflow occurs when a program writes more data into a fixed-size memory region than it was designed to hold. The excess data spills into adjacent memory, potentially overwriting other variables, control structures, or return addresses. In C and C++, this class of bug is responsible for some of the most severe exploits in computing history — from the Morris Worm in 1988 to modern IoT device compromises.

This vulnerability is classified as CWE-120: Buffer Copy without Checking Size of Input ("Classic Buffer Overflow").

Where Was the Bug?

In components/meshtastic/meshtastic.cpp, around line 449, the firmware handled incoming encrypted radio packets like this:

// VULNERABLE CODE (before fix)
memcpy(radio_buffer.payload, mp->encrypted.bytes, mp->encrypted.size);

Let's unpack what's happening here:

  • radio_buffer.payload is a fixed-size buffer allocated on the stack or heap.
  • mp->encrypted.bytes is the raw encrypted payload from an incoming radio packet.
  • mp->encrypted.size is the attacker-controlled length field from that same packet.

The problem? There is no check that mp->encrypted.size is actually smaller than sizeof(radio_buffer.payload).

If an attacker sends a packet with encrypted.size set to 0xFFFF (65,535 bytes), the firmware will dutifully copy up to 65,535 bytes from the incoming packet data into a buffer that might only be 256 bytes wide. Everything past that 256-byte boundary gets overwritten — and that memory belongs to something else.

The Trust Problem: No Authentication on the Mesh

What makes this particularly dangerous is the threat model of Meshtastic itself. Mesh nodes do not authenticate each other. Any device with a compatible radio can broadcast packets that will be received and processed by nearby nodes. There's no TLS handshake, no certificate validation, no "is this a trusted sender?" check before transmit_radio_packet() is called.

This means the attack surface is:

  • Any node within direct radio range (~2–15 km depending on terrain and antenna)
  • Any node reachable through mesh relay — potentially much farther, as Meshtastic packets are relayed hop-by-hop

What Could an Attacker Do?

Depending on the memory layout of the target device (typically an ESP32), a successful overflow could:

  1. Corrupt heap metadata, causing undefined behavior or a crash (Denial of Service)
  2. Overwrite adjacent stack variables, changing program logic
  3. Overwrite a return address or function pointer, leading to arbitrary code execution
  4. Brick the device by corrupting critical firmware state

In the worst case, an attacker within radio range — or connected through the mesh — could achieve remote code execution on your ESP32 node without any physical access and without any credentials.

A Concrete Attack Scenario

Imagine Alice is running a Meshtastic node at a remote campsite for emergency communication. Bob, a malicious actor, is parked 3 km away with a compatible LoRa radio and a laptop. Bob crafts a raw Meshtastic packet with:

encrypted.size = 0xFFFF  // 65535 bytes
encrypted.bytes = [carefully crafted payload]

Bob transmits this packet. Alice's node receives it, calls transmit_radio_packet(), and the vulnerable memcpy fires — copying 65,535 bytes into a small fixed buffer. Depending on what Bob put in those bytes, he may have just taken control of Alice's device.


The Fix

The fix is elegant in its simplicity. Before performing the memcpy, a bounds check was added:

Before (Vulnerable)

memcpy(radio_buffer.payload, mp->encrypted.bytes, mp->encrypted.size);

After (Fixed)

if (mp->encrypted.size > sizeof(radio_buffer.payload)) {
    ESP_LOGE(TAG, "Encrypted payload too large: %u > %u",
             mp->encrypted.size, sizeof(radio_buffer.payload));
    return false;
}
memcpy(radio_buffer.payload, mp->encrypted.bytes, mp->encrypted.size);

Why This Works

The fix does three important things:

  1. Validates the length before use: mp->encrypted.size is compared against the actual size of the destination buffer using sizeof(). This is a compile-time constant, so it will always reflect the true buffer size even if the code is refactored later.

  2. Fails safely: Instead of attempting the copy and corrupting memory, the function logs an error and returns false. The packet is silently dropped. No crash, no corruption, no exploitation.

  3. Provides observability: The ESP_LOGE call logs the oversized packet with the actual sizes, which is invaluable for debugging and for detecting active exploitation attempts in the field.

The Diff at a Glance

+  if (mp->encrypted.size > sizeof(radio_buffer.payload)) {
+    ESP_LOGE(TAG, "Encrypted payload too large: %u > %u",
+             mp->encrypted.size, sizeof(radio_buffer.payload));
+    return false;
+  }
   memcpy(radio_buffer.payload, mp->encrypted.bytes, mp->encrypted.size);

Four lines. That's all it took to close a critical remote code execution vector.


Prevention & Best Practices

This vulnerability follows a well-worn pattern. Here's how to avoid it in your own embedded C/C++ code:

1. Always Validate Lengths Before memcpy / strcpy / sprintf

Any time you copy data into a fixed-size buffer, ask: "Where does the length come from?" If it comes from user input, a network packet, or any external source — validate it first.

// Bad
memcpy(dest, src, untrusted_length);

// Good
if (untrusted_length > sizeof(dest)) {
    return ERROR_TOO_LARGE;
}
memcpy(dest, src, untrusted_length);

2. Prefer sizeof() Over Magic Numbers

Using sizeof(buffer) instead of a hardcoded constant ensures your bounds check stays correct if the buffer size changes during refactoring.

// Fragile — breaks if PAYLOAD_SIZE changes
if (len > 256) { ... }

// Robust — always correct
if (len > sizeof(radio_buffer.payload)) { ... }

3. Use Safer Alternatives Where Possible

In higher-level C++ code, prefer std::vector, std::string, or std::span over raw buffers. For C-style copies, consider:

  • memcpy_s() (bounds-checking variant, available in C11 Annex K)
  • std::copy with range checks
  • Custom wrapper functions that enforce size limits

4. Treat All Network Input as Hostile

In embedded networking code, every field in every packet is attacker-controlled. Length fields, type fields, flags — all of it. Apply the principle of "trust nothing from the network" consistently.

5. Enable Compiler and Runtime Protections

Modern compilers and runtimes offer mitigations that can reduce the impact of buffer overflows:

Protection How to Enable What It Does
Stack canaries -fstack-protector-all (GCC/Clang) Detects stack smashing at runtime
AddressSanitizer -fsanitize=address (debug builds) Catches out-of-bounds accesses
FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 Adds bounds checks to libc functions
Static analysis Clang-Tidy, Coverity, CodeQL Finds bugs before runtime

For ESP32/ESP-IDF projects specifically, enabling CONFIG_COMPILER_STACK_CHECK_MODE_STRONG in menuconfig adds stack overflow detection.

6. Fuzz Your Packet Parsers

Tools like AFL++ and libFuzzer can automatically generate malformed inputs to find buffer overflows before attackers do. If your firmware parses radio packets, fuzz the parser.

Security Standards & References

  • CWE-120: Buffer Copy without Checking Size of Input — https://cwe.mitre.org/data/definitions/120.html
  • OWASP: Buffer Overflow — https://owasp.org/www-community/vulnerabilities/Buffer_Overflow
  • SEI CERT C Coding Standard: ARR38-C — Guarantee that library functions do not form invalid pointers
  • MISRA C:2012: Rule 21.17 — Use of string handling functions

Conclusion

This vulnerability is a textbook example of why input validation is non-negotiable in networked embedded systems. A single missing bounds check turned a routine memcpy into a remote code execution vector exploitable by anyone with a LoRa radio.

The fix is four lines of code. The potential damage without it? Complete compromise of every vulnerable Meshtastic node within mesh reach.

Key takeaways:

  • Never trust length fields from external sources — always validate against your actual buffer size.
  • Fail safely — when validation fails, return an error and drop the input rather than proceeding with dangerous operations.
  • Log anomalies — oversized packets are a sign of either bugs or active attacks; make them visible.
  • The attack surface of mesh networks is wide — unauthenticated, multi-hop radio means your threat model must include remote, anonymous attackers.

Secure coding in embedded systems isn't glamorous, but it's essential. The next time you reach for memcpy, take two seconds to ask: "Did I check the length?" Your users — and their devices — will thank you.


This vulnerability was identified and fixed by OrbisAI Security. The fix has been verified by automated re-scan and LLM code review.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #2

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