From 4e33c0932a4780bba67ef4d509548d77ce706f6b Mon Sep 17 00:00:00 2001 From: Wessel Nieboer Date: Wed, 11 Feb 2026 04:34:54 +0100 Subject: [PATCH] Bounds-check reply_path in anonymous request handlers The handleAnon*Req functions read a reply_path_len byte from the decrypted data and memcpy that many bytes into reply_path, without checking that the data buffer actually contains that many bytes. With a minimal-length packet, this reads up to 63 bytes of uninitialized stack memory. Add a data_len parameter to all three handlers and validate that the buffer contains enough bytes for the claimed reply_path_len before copying. Also guard the callers to ensure len > 5 before passing &data[5]. --- examples/simple_repeater/MyMesh.cpp | 24 +++++++++++++++--------- examples/simple_repeater/MyMesh.h | 6 +++--- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 65e0cee52..4a3fa515d 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -144,10 +144,12 @@ uint8_t MyMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t* secr return 13; // reply length } -uint8_t MyMesh::handleAnonRegionsReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data) { +uint8_t MyMesh::handleAnonRegionsReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data, size_t data_len) { if (anon_limiter.allow(rtc_clock.getCurrentTime())) { // request data has: {reply-path-len}{reply-path} + if (data_len < 1) return 0; reply_path_len = *data++ & 0x3F; + if (1 + reply_path_len > data_len) return 0; memcpy(reply_path, data, reply_path_len); // data += reply_path_len; @@ -160,10 +162,12 @@ uint8_t MyMesh::handleAnonRegionsReq(const mesh::Identity& sender, uint32_t send return 0; } -uint8_t MyMesh::handleAnonOwnerReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data) { +uint8_t MyMesh::handleAnonOwnerReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data, size_t data_len) { if (anon_limiter.allow(rtc_clock.getCurrentTime())) { // request data has: {reply-path-len}{reply-path} + if (data_len < 1) return 0; reply_path_len = *data++ & 0x3F; + if (1 + reply_path_len > data_len) return 0; memcpy(reply_path, data, reply_path_len); // data += reply_path_len; @@ -177,10 +181,12 @@ uint8_t MyMesh::handleAnonOwnerReq(const mesh::Identity& sender, uint32_t sender return 0; } -uint8_t MyMesh::handleAnonClockReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data) { +uint8_t MyMesh::handleAnonClockReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data, size_t data_len) { if (anon_limiter.allow(rtc_clock.getCurrentTime())) { // request data has: {reply-path-len}{reply-path} + if (data_len < 1) return 0; reply_path_len = *data++ & 0x3F; + if (1 + reply_path_len > data_len) return 0; memcpy(reply_path, data, reply_path_len); // data += reply_path_len; @@ -518,12 +524,12 @@ void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const m reply_path_len = -1; if (data[4] == 0 || data[4] >= ' ') { // is password, ie. a login request reply_len = handleLoginReq(sender, secret, timestamp, &data[4], packet->isRouteFlood()); - } else if (data[4] == ANON_REQ_TYPE_REGIONS && packet->isRouteDirect()) { - reply_len = handleAnonRegionsReq(sender, timestamp, &data[5]); - } else if (data[4] == ANON_REQ_TYPE_OWNER && packet->isRouteDirect()) { - reply_len = handleAnonOwnerReq(sender, timestamp, &data[5]); - } else if (data[4] == ANON_REQ_TYPE_BASIC && packet->isRouteDirect()) { - reply_len = handleAnonClockReq(sender, timestamp, &data[5]); + } else if (data[4] == ANON_REQ_TYPE_REGIONS && packet->isRouteDirect() && len > 5) { + reply_len = handleAnonRegionsReq(sender, timestamp, &data[5], len - 5); + } else if (data[4] == ANON_REQ_TYPE_OWNER && packet->isRouteDirect() && len > 5) { + reply_len = handleAnonOwnerReq(sender, timestamp, &data[5], len - 5); + } else if (data[4] == ANON_REQ_TYPE_BASIC && packet->isRouteDirect() && len > 5) { + reply_len = handleAnonClockReq(sender, timestamp, &data[5], len - 5); } else { reply_len = 0; // unknown/invalid request type } diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 7a51b4a97..3f0af7b4e 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -117,9 +117,9 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr); uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood); - uint8_t handleAnonRegionsReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data); - uint8_t handleAnonOwnerReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data); - uint8_t handleAnonClockReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data); + uint8_t handleAnonRegionsReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data, size_t data_len); + uint8_t handleAnonOwnerReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data, size_t data_len); + uint8_t handleAnonClockReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data, size_t data_len); int handleRequest(ClientInfo* sender, uint32_t sender_timestamp, uint8_t* payload, size_t payload_len); mesh::Packet* createSelfAdvert();