Skip to content

cli: reject a negative sensor list start index - #100

Merged
liquidraver merged 1 commit into
liquidraver:devfrom
ptr727:sensor-list-bounds
Oct 5, 2026
Merged

liquidraver merged 1 commit into
liquidraver:devfrom
ptr727:sensor-list-bounds

Conversation

@ptr727

@ptr727 ptr727 commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

The MeshCore counterpart is meshcore-dev/MeshCore#3433.

The defect

sensor list <start> parses start with _atoi(), which returns a uint32_t, into an int. A start of 4294967295 becomes -1, and 2147483648 becomes INT_MIN. Both pass the start >= end guard, so the loop asks the sensor manager for a negative index, gets NULL back, and passes it to "%s=%s\n". That is undefined behaviour; picolibc happens to print (null). sensor list is an admin command, so a remote admin can reach this.

The fix

if (start < 0 || start >= end), as suggested in review. ZephyrSensorManager has one setting, gps, so only index 0 is ever requested and no NULL reaches %s.

Not changed: the page loop bounds snprintf with CLI_REPLY_SIZE (256), while a remote reply has 161 bytes. A long setting near the end of a page could overrun it, but no sensor manager here has one.

Testing

  • rak4631 repeater, companion and room server build with no compiler warnings.
  • Not run on hardware.

Fork issue: ptr727#43. Iteration history and review: ptr727#44.

🤖 Generated with Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

No unresolved review issues remain.

Review effort: Lite
Findings: None

What changed in this PR

This pull request hardens sensor list against invalid indices, unsafe NULL formatting, and oversized replies.

Changes:

  • Safely parses and bounds start indices.
  • Handles NULL sensor entries and bounds pagination.
  • Documents paging and out-of-range behavior.
File Summary
zephcore/​helpers/​CommonCLI.cpp Implements safe parsing, NULL handling, and bounded paging.
docs/​Repeater_CLI_commands.md Documents updated sensor-list behavior.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@liquidraver

Copy link
Copy Markdown
Owner

isn't this a little over-engineered?
Everything reachable today is closed by rejecting the negative start:

if (start < 0 || start >= end) {

ptr727 added a commit to ptr727/liquidraver-ZephCore that referenced this pull request Oct 5, 2026
The owner asked whether the fix was over-engineered, on
liquidraver#100. It was. ZephyrSensorManager has one setting,
gps, so the negative start from _atoi()'s uint32_t is the only fault
reachable today, and `start < 0 ||` closes it: only index 0 is ever
requested, so no NULL reaches "%s". The parser clamp, the page bound
and the docs row are reverted to dev.

Left as is: the page loop bounds snprintf with CLI_REPLY_SIZE while a
remote reply has 161 bytes. A long setting near the end of a page could
overrun it, but no such setting exists.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`sensor list <start>` parsed start with _atoi(), whose uint32_t goes
negative as an int: a start of 4294967295 is -1 and 2147483648 is
INT_MIN. Both passed the `start >= end` guard, so the loop asked the
sensor manager for a negative index and passed the NULL it returned
to "%s=%s", undefined behaviour that picolibc happens to print as
"(null)". Rejecting a negative start closes it: ZephyrSensorManager
has one setting, so only index 0 is ever requested.

The same defect as meshcore-dev/MeshCore#3433.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ptr727
ptr727 force-pushed the sensor-list-bounds branch from 426dc96 to 8190b40 Compare October 5, 2026 19:14
@ptr727 ptr727 changed the title cli: bound sensor list's start index and its pages cli: reject a negative sensor list start index Oct 5, 2026
@ptr727

ptr727 commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

Fair point, yes. ZephyrSensorManager has one setting, gps, so the negative start is the only thing reachable today, and start < 0 || closes it. With only index 0 ever requested, the NULL never reaches %s either.

The rest was hardening for a sensor manager with more or longer settings. One latent issue worth knowing about: the page loop bounds snprintf with CLI_REPLY_SIZE (256), but a remote reply has 161 bytes, so a long setting near the end of a page could write past it. That can't happen with gps=1.

Cut down to your line in 8190b40, and the description is updated to match.

@liquidraver
liquidraver merged commit d1cc647 into liquidraver:dev Oct 5, 2026
1 check passed
@ptr727
ptr727 deleted the sensor-list-bounds branch October 5, 2026 20:30
ptr727 added a commit to ptr727/meshcore-dev-MeshCore that referenced this pull request Oct 5, 2026
`sensor list <n>` passes two NULL pointers to sprintf("%s=%s\n", ...)
for any n in [2147483648, 4294967295]. On the C libraries these targets
link against, that faults rather than printing "(null)".

_atoi() returns uint32_t, and its result goes straight into an int
start, so `sensor list 4294967295` gives start == -1. The guard
`start >= end` can't catch that: -1 >= end is false for every setting
count, end == 0 included. The loop then runs from a negative index,
where getSettingName() and getSettingValue() return NULL.

The branch sits behind no #if, and the base SensorManager accessors
always return NULL, so a plain repeater with no sensors is affected
too. The command also works over remote admin. The effect depends on
the target: an ESP32-S3 panics (LoadProhibited at address 0) and
reboots, while an nRF52840 hangs until someone resets it by hand.

Add `start < 0` to the guard, so a wrapped start gets the existing
"no custom var" reply. This is the same line as liquidraver/ZephCore#100.

Every sensor manager on dev exposes at most one setting, `gps`, so the
paging loop's fixed 134-byte bound can't overrun today. That bound is
left as it is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ptr727 added a commit to ptr727/meshcore-dev-MeshCore that referenced this pull request Oct 5, 2026
Every sensor manager on dev exposes at most one setting, so the paging
bound and the start-index clamp guard nothing reachable. Restore dev's
parser, paging loop and docs, and keep only the check that stops a
wrapped negative start reaching the accessors. Matches
liquidraver/ZephCore#100 as merged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ptr727 added a commit to ptr727/liquidraver-ZephCore that referenced this pull request Oct 6, 2026
dev brought liquidraver#99's RTC identification (rtc_identify() and its verdicts),
liquidraver#98's RAK12002 and RV3028 EEPROM configuration, liquidraver#100, and the rename of
docs/Repeater_CLI_commands.md to docs/CLI_commands.md. The hw docs moved
with the rename without conflict.

ZephyrRTCDiscover.c conflicted where hw recorded a report state at the
inline reads liquidraver#99 replaced. dev's logic is taken whole, and each verdict
now maps to a report state: an address NACK or RTC_NOT_THIS is absent,
RTC_FOUND and RTC_FOUND_GARBLED are present. rtc_identify() gains
RTC_UNREAD for a bus that is not ready or a read failing other than with
-EIO, which keeps hw's "unprobed" for those; discovery skips it exactly
as it skips RTC_ABSENT.

liquidraver#99's all-0xFF skip is neither absent nor present, so it gets its own
state, rendered "all 0xff". It counts with unprobed toward the summary,
now worded "unsettled", since neither settles whether an RTC is fitted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants