From 65b5371a65e9a5821313297e00ace90802daae03 Mon Sep 17 00:00:00 2001 From: liquidraver <504870+liquidraver@users.noreply.github.com> Date: Sun, 31 May 2026 15:36:31 +0200 Subject: [PATCH] fix(repeater): flood anon reply when the return path is rejected The three anon handlers ignored copyPath's return value: on a rejected (over-long) path, reply_path_len kept the attacker byte while reply_path stayed stale, so the reply went out with a corrupt direct path. Reset to OUT_PATH_UNKNOWN so it floods instead; keep the legit zero-length case. --- zephcore/app/RepeaterMesh.cpp | 36 +++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/zephcore/app/RepeaterMesh.cpp b/zephcore/app/RepeaterMesh.cpp index 76ba070..408578f 100644 --- a/zephcore/app/RepeaterMesh.cpp +++ b/zephcore/app/RepeaterMesh.cpp @@ -201,8 +201,16 @@ uint8_t RepeaterMesh::handleAnonRegionsReq(const mesh::Identity& sender, uint32_ if (data_len < 1) return 0; reply_path_len = *data++; data_len--; - /* data is anon-req-supplied; bound copy with remaining data_len. */ - mesh::Packet::copyPath(reply_path, data, data_len, reply_path_len); + /* data is anon-req-supplied; bound copy with remaining data_len. + * If the claimed path is longer than the bytes provided, copyPath + * rejects (returns 0) and leaves reply_path stale — fall back to a + * flood reply instead of emitting a corrupt direct path (F2). + * path_len == 0 is a valid zero-hop direct reply, so don't treat + * its (also-0) return as a rejection. */ + if (reply_path_len != 0 && + mesh::Packet::copyPath(reply_path, data, data_len, reply_path_len) == 0) { + reply_path_len = OUT_PATH_UNKNOWN; + } memcpy(reply_data, &sender_timestamp, 4); uint32_t now = getRTCClock()->getCurrentTime(); @@ -218,8 +226,16 @@ uint8_t RepeaterMesh::handleAnonOwnerReq(const mesh::Identity& sender, uint32_t if (data_len < 1) return 0; reply_path_len = *data++; data_len--; - /* data is anon-req-supplied; bound copy with remaining data_len. */ - mesh::Packet::copyPath(reply_path, data, data_len, reply_path_len); + /* data is anon-req-supplied; bound copy with remaining data_len. + * If the claimed path is longer than the bytes provided, copyPath + * rejects (returns 0) and leaves reply_path stale — fall back to a + * flood reply instead of emitting a corrupt direct path (F2). + * path_len == 0 is a valid zero-hop direct reply, so don't treat + * its (also-0) return as a rejection. */ + if (reply_path_len != 0 && + mesh::Packet::copyPath(reply_path, data, data_len, reply_path_len) == 0) { + reply_path_len = OUT_PATH_UNKNOWN; + } memcpy(reply_data, &sender_timestamp, 4); uint32_t now = getRTCClock()->getCurrentTime(); @@ -236,8 +252,16 @@ uint8_t RepeaterMesh::handleAnonClockReq(const mesh::Identity& sender, uint32_t if (data_len < 1) return 0; reply_path_len = *data++; data_len--; - /* data is anon-req-supplied; bound copy with remaining data_len. */ - mesh::Packet::copyPath(reply_path, data, data_len, reply_path_len); + /* data is anon-req-supplied; bound copy with remaining data_len. + * If the claimed path is longer than the bytes provided, copyPath + * rejects (returns 0) and leaves reply_path stale — fall back to a + * flood reply instead of emitting a corrupt direct path (F2). + * path_len == 0 is a valid zero-hop direct reply, so don't treat + * its (also-0) return as a rejection. */ + if (reply_path_len != 0 && + mesh::Packet::copyPath(reply_path, data, data_len, reply_path_len) == 0) { + reply_path_len = OUT_PATH_UNKNOWN; + } memcpy(reply_data, &sender_timestamp, 4); uint32_t now = getRTCClock()->getCurrentTime();