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 Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published May 28, 2026•Reviewed June 3, 2026

Answer Summary

This is a buffer overflow vulnerability (CWE-120) in the Meshtastic firmware's C-based radio packet handler, where an unchecked `memcpy` operation trusted the attacker-controlled payload length field from incoming LoRa packets. Any unauthenticated node on the mesh — or one hop away via relay — could send a crafted packet to overwrite adjacent memory, potentially enabling remote code execution. The fix adds a bounds check that validates the incoming payload length against the maximum allowed buffer size before the `memcpy` executes, preventing out-of-bounds writes entirely.

Vulnerability at a Glance

cweCWE-120
fixAdd a bounds check comparing the packet's payload length against the maximum buffer size before memcpy executes
riskRemote code execution by any unauthenticated node within radio or mesh relay range
languageC (Embedded Firmware)
root causememcpy called with an attacker-controlled length field from an incoming radio packet, with no bounds validation
vulnerabilityBuffer Overflow via unchecked memcpy in radio packet handler

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.


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.

Prevention and further reading

Related Articles

critical

Aardvark.Cef.Process.Core Shared Memory Handler: Unvalidated memcpy

The shared memory handler in Aardvark.Cef.Process.Core failed to validate that the `length` parameter passed from JavaScript did not exceed the destination buffer size before calling `memcpy`. This allowed a compromised Chromium renderer process to corrupt heap memory by supplying an oversized `handle->length` value to the `openMapping` function. The fix adds explicit bounds checking using `GetArrayBufferByteLength()` before the copy operation.

high

XShmGetImage Heap Corruption: Unvalidated Image Height in streamproxy

The `XShmGetImage` function in the X11 shared memory image path copies pixel data row-by-row using `memcpy` without validating that the source image height matches the destination buffer height. An attacker or compromised server could provide an oversized source image, causing writes beyond the allocated heap buffer and triggering heap corruption or code execution.

critical

SGX Enclave ecall_store_data memcpy Buffer Overflow in 256-Byte

The Intel SGX enclave's trusted bridge functions `ecall_store_data` and `ecall_retrieve_data` used `memcpy()` to move data into and out of a fixed 256-byte `secure_storage` buffer without validating that `data_len` fit within destination boundaries. An attacker providing oversized `data_len` values could corrupt enclave memory, breaking SGX's confidentiality guarantees.

critical

How Buffer Overflow via strcpy() Happens in C++ XML Parsers and How to Fix It

A critical buffer overflow vulnerability was discovered in `buildroot-external/package/libxmlparser/xmlParser.cpp`, where the `toXMLString` function used `_tcscpy()` to write XML escape sequences into a destination buffer without any bounds checking. An attacker supplying a crafted XML document could overflow the buffer and potentially execute arbitrary code. The fix replaces all five unsafe `_tcscpy()` calls with `memcpy()` calls that copy only the exact number of bytes required for each escape

critical

How Heap Buffer Overflows Happen in C++ ZIP Extraction and How to Fix Them

A critical heap buffer overflow vulnerability was discovered in `TKLiveSync/unzip.cpp`, where ZIP archive entry names were copied into a `PATH_MAX`-sized heap buffer using `strcpy()` without any length validation. Since the ZIP specification allows entry names up to 65,535 bytes — far exceeding typical `PATH_MAX` values of 1,024 to 4,096 bytes — a crafted archive could overflow the buffer and corrupt heap memory. The fix replaces the unsafe `strcpy`/`dirname` pattern with `std::string` operation

medium

How Integer Overflow happens in C++ image processing and how to fix it

A signed integer overflow in OpenCV's `bilateralFilter.cpp` allowed the buffer size calculation `cal_width * cal_height * cn` to wrap around to a small or negative value, causing `padding.resize()` to allocate far less memory than needed. Subsequent `memcpy` operations would then write beyond the allocated buffer, creating a heap corruption primitive. The fix is a single targeted cast to `size_t` that promotes the multiplication to unsigned 64-bit arithmetic before any overflow can occur.