Repository navigation
Fix remaining buffer overflows in the CLI command paths - #3848
Open
Valentin-Metz wants to merge 1 commit into
Open
Valentin-Metz wants to merge 1 commit into
Valentin-Metz wants to merge 1 commit into
Conversation
Follow-up to meshcore-dev#3807: auditing the full CLI dispatch chain for the same class of overflow turned up more sites, all reachable with the same long-input technique (remote datagrams decrypt to commands of up to 171 chars; the serial/ethernet buffers accept up to 159): - CommonRadioPrefs::handleCommand 'set radio' copied the command tail into a 132-byte stack tmp[] with strcpy() before any parameter validation -- ~30 bytes past the end (remote admin CLI, no prefix needed). - CommonCLI::handleCommand 'tempradio' / 'sensor set' and handleSetCmd 'extra.sf' copied into the 132-byte member tmp[] with strcpy(), corrupting adjacent members of the node object. - CommonCLI::handleGetCmd's unknown-config fallback did sprintf(reply, "??: %s", config) -- a remote 'get <167 chars>' writes 8 bytes past the temp[169] sized in meshcore-dev#3807. - The serial/ethernet reply buffers in the repeater/room-server/sensor examples were char[160] but feed the same prefix-reflecting handleCommand() fixed in meshcore-dev#3807: a prefixed 'aa|regions' writes 3 + 160 = 163 bytes plus NUL. Now 164 with the same sizing comment. - simple_secure_chat 'public <msg>' sprintf'ed node_name + user text into a 160-byte region and only truncated after the fact -- a long serial line overflows by ~350 bytes before the truncation runs. Replaced with a bounded snprintf. - DynamicConfigSerializer::getByKey() terminated with value[max_len] = 0 -- one past the end of a max_len-sized buffer for any caller passing sizeof(buf) (current callers pass sizeof(buf)-1, so latent). Bounds the copies with StrHelper::strncpy()/snprintf() following the existing repo idiom, and sizes the example reply buffers for the full worst case. Verified with a host ASan harness compiling the real CommonCLI, CommonRadioPrefs and RegionMap sources with the exact reply-buffer layouts of onPeerDataRecv() and the serial main loops: 'set radio' + 171 junk chars, 'tempradio'/'sensor set' + max-length input and 'get' + 167 junk chars each overflowed before this change and pass clean after it; the secure_chat sprintf-then-truncate shape likewise. pio test -e native: 50/50 pass (incl. config serializer getByKey).
Valentin-Metz
force-pushed
the
fix/cli-parse-buffer-overflows
branch
from
November 15, 2026 06:07
443fb61 to
289f4dc
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #3807. Auditing the full CLI dispatch chain (
onPeerDataRecv→handleCommand→CommonCLI/CommonRadioPrefs→ reply buffer) for the same class of overflow turned up more sites, reachable with the same long-input technique. Remote datagrams decrypt to commands of up to 171 chars; the serial/ethernet input loops accept up to 159.Overflow sites fixed
CommonRadioPrefs.cppset radioset radio+ ≥123 junk charsCommonCLI.cpptempradiotempradio+ junktmp[]→ corrupts adjacent members of the node objectCommonCLI.cppsensor setsensor set+ junkCommonCLI.cppextra.sf(USE_LR2021builds)set extra.sf+ junkCommonCLI.cppget <unknown>fallbackget+ 167 junk charstemp[169]sized in #3807 — the previous fix is defeated by this pathmain.cppreply[160]aa|regionshandleCommand()fixed in #3807, but weren't resizedsimple_secure_chatpublic <msg>sprintffirst, truncation line runs only after the overflowDynamicConfigSerializer::getByKeysizeof(buf)value[max_len] = 0writes one past amax_len-sized buffer (latent: current callers passsizeof(buf)-1)Changes
strcpy→StrHelper::strncpy(tmp, …, sizeof(tmp))at the fourtmp[]copy sites (existing repo idiom; truncation is safe — the copied text is only parsed for numeric params/keys).sprintf(reply, "??: %s", config)→snprintf(reply, 160, …).char reply[160]→char reply[164]in the example serial/ethernet loops (3-byte reflected prefix + 160-byte reply + NUL), same comment style as Fix stack-buffer overflow in CLI reply buffers #3807.simple_secure_chat: sprintf-then-truncate → boundedsnprintf.getByKey:strncpy(value, …, max_len - 1); value[max_len - 1] = 0;.Reachability
tmp[]andget ???sites are reachable over the remote admin CLI (LoRa datagram), and also from serial and the ethernet TCP CLI (ETHERNET_ENABLEDbuilds).main.cppreply[164]buffers cover serial + ethernet.secure_chatis serial-only.getByKeyis latent.Verification
Host ASan harness compiling the real
CommonCLI.cpp,CommonRadioPrefs.cpp,RegionMap.cppetc. with the exact reply-buffer layouts ofonPeerDataRecv()and the serialmain()loops:stack-buffer-overflowon all four patchable paths —CommonRadioPrefs.cpp:57(set radio),CommonCLI.cpp:255(tempradio),CommonCLI.cpp:311(sensor set),CommonCLI.cpp:822(get ???, 8 bytes pasttemp[169]); the secure_chat sprintf-then-truncate shape likewise.Error, invalid radio params, etc.), prefixedregionslisting unaffected (reply_len=162).pio test -e native: 50/50 pass, includingtest_config_serializerwhich exercisesgetByKey.