AdminModule: only accept admin responses to requests we sent#11024
AdminModule: only accept admin responses to requests we sent#11024caveman99 wants to merge 100 commits into
Conversation
Current release tags are actually based upon the latest state of `develop` currently... Specify target_commitish to always use the commit that triggered the build
Adjust keyboard cell height calculation for better layout consistency across different screen sizes. Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
…gs and prevent location leaks (#10513)
For idf5 compat
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Manuel <71137295+mverch67@users.noreply.github.com>
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* Add Lilygo T-Impulse-Plus * Enable small screen layout 64x32 * trunk'd * Haptic Feedback (short and long press) * enable Charging Indicator * enable nrfutil uploads * trunk fmt * Add Lilygo T-Impulse-Plus * Enable small screen layout 64x32 * trunk'd * Haptic Feedback (short and long press) * enable Charging Indicator * enable nrfutil uploads * trunk fmt * enable proper device model * Dim the haptic duration a bit. * Fix GPS pins and speed. Module enable is active low, speed is 38400 * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * set correct geometry. * Add custom_meshtastic_* metadata to t-impulse-plus platformio.ini --------- Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com> Co-authored-by: Ben Meadors <benmmeadors@gmail.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* add heltec-mesh-node-t1 * fixed low power * Update the sensor enumeration values. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Fix memory leak in ICM42607PSensor * fix ST7735_MISO error --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Infinite calibration loop fix * Save calibration * Screen refresh * reduce repeated code * reduce repeated code to reduce flash * fix Waypoint compass size and no fix no heading labels * Don't show compass unless we have a heading and location * If no calculated heading from moving, we should have no heading * Slow walking calculated heading and auto stale heading when not moving * Triming flash space * cleanup * show "?" when no location or heading for distance and heading screen * cleanup * Stale heading logic * final trim * Compass Calibration screen redesign * Trunk Fix * Compile fix * patch * Update src/motion/MotionSensor.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update WaypointModule.cpp --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
… with SparkFun MMC5983MA (#10584) * Initial plan * Fix SensorLib isBitSet macro conflict with SparkFun MMC5983MA library SensorLib 0.3.4 defines isBitSet as a C preprocessor macro in SensorLib.h, which conflicts with SparkFun_MMC5983MA_IO.h's class method of the same name. When both libraries are included in the same translation unit (e.g., via configuration.h → SensorRtcHelper.hpp → SensorLib chain, alongside the SparkFun MMC5983MA library in lib_deps), the macro expansion causes compile errors like 'expected unqualified-id before const'. Fix: undefine the isBitSet macro right after including SensorRtcHelper.hpp in configuration.h, so it doesn't interfere with SparkFun's class method. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Trunk isn't properly updating the `v1` tag in-place. Switch to specific tags and let renovate handle the rest. Maybe
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
* rebased to master * Update bin/config.d/lora-ZebraHat_2W.yaml Co-authored-by: Austin <vidplace7@gmail.com> * Update bin/config.d/lora-ZebraHat_1W.yaml Co-authored-by: Austin <vidplace7@gmail.com> * Update bin/config.d/lora-NebraHat_2W.yaml Co-authored-by: Austin <vidplace7@gmail.com> * Update bin/config.d/lora-NebraHat_1W.yaml Co-authored-by: Austin <vidplace7@gmail.com> * Remove TX_GAIN_LORA configuration line * Remove TX_GAIN_LORA configuration line * Comment out TX_GAIN_LORA configuration * Comment out TX_GAIN_LORA configuration * Update lora-ok3506-RAK6421-13300-slot1.yaml * Update lora-ok3506-RAK6421-13300-slot2.yaml * Update lora-RAK6421-13300-slot1.yaml * Update lora-RAK6421-13300-slot2.yaml * Added Zebra Hat Duo --------- Co-authored-by: Austin <vidplace7@gmail.com>
…ardputer Advanced (#10884) * Implement SD card backup/restore of preferences; add SD support for Cardputer Advanced Fills the two 'TODO: After more mainline SD card support' stubs in NodeDB::backupPreferences / restorePreferences so the existing backup_preferences / restore_preferences admin messages work with BackupLocation_SD. Same BackupPreferences proto as the FLASH location, stored at /backups/backup.proto on the card; guarded by HAS_SDCARD && !SDCARD_USE_SOFT_SPI; SD access under spiLock. Also defines the SD card slot pins for the M5Stack Cardputer Advanced (shares the SPI bus with the SX1262, separate CS), whose physical slot was previously unused by the firmware. Hardware-tested on two Cardputer Advanced units (as a v2.7.26 backport): mount, backup write, and full restore (config, module config, channels, owner, security keys) verified; LoRa unaffected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Deduplicate backup construction and restore application Extract buildBackupPreferences() and applyRestoredPreferences() shared by the FLASH and SD locations, per review feedback, so the two paths cannot drift apart. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Maksym Tereshko <maxim.tereshko@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Maksym Tereshko <termax@ter-air-12.taila94f88.ts.net>
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
An admin *_response short-circuited the auth and session-passkey checks that gate every other admin message, so any node could deliver one. On a channel the module listens to unauthenticated, a get_module_config_response drives the remote-hardware pin handler with attacker-supplied values. Track the destination of outgoing admin requests (per remote, with the pinned PKC key when there is one) and accept a response only from a node with a matching outstanding request, inside the same window as the session passkey. Local (from == 0) admin is unchanged; PhoneAPI already gates it. Also fix the response dispatch: get_module_config_response.which_payload_variant is a ModuleConfig oneof tag, but it was compared against the AdminMessage ModuleConfigType enum (different numbering), so the handler never ran. Compare against the oneof tag.
📝 WalkthroughWalkthroughAdmin requests are tracked by destination, timeout, response variant, and optional PKC key. Outgoing mesh handling records requests, while incoming remote responses are rejected unless solicited. Remote hardware response matching is corrected and covered by expanded tests. BLE restoration, board configuration, release metadata, and workflow updates are also included. ChangesAdmin response solicitation
Platform and release updates
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant MeshService
participant AdminModule
participant Mesh
MeshService->>AdminModule: Record outgoing admin request
MeshService->>Mesh: Forward packet
Mesh->>AdminModule: Deliver admin response
AdminModule->>AdminModule: Validate solicitation and key
AdminModule->>AdminModule: Apply remote hardware response
Possibly related issues
Suggested labels: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/modules/AdminModule.cpp`:
- Around line 1852-1853: Replace the raw sentAtSecs/millis-based expiry
arithmetic in the request response gate around slot->sentAtSecs and the related
lines 1864-1870 with the repository’s Throttle helper. Initialize and check
Throttle using elapsed-time semantics so the 300-second request expiry remains
correct across millis() rollover, while preserving the existing response
handling behavior.
In `@src/modules/AdminModule.h`:
- Around line 90-99: Update OutstandingAdminRequest and responseIsSolicited to
store and validate the expected response variant/tag alongside the destination
and key, so a request only authorizes its matching response type within the
existing timeout. Populate the expected tag when recording each getter request,
reject mismatched response tags, and add a test confirming an otherwise valid
response with the wrong variant is denied.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 8b8d9011-19d7-450e-bd7f-873840dc1d28
📒 Files selected for processing (5)
src/mesh/MeshService.cppsrc/modules/AdminModule.cppsrc/modules/AdminModule.htest/support/AdminModuleTestShim.htest/test_admin_session_repro/test_main.cpp
⚡ Try this PR in the Web FlasherNote Building this pull request… the flash button, badges and supported-board |
Two review refinements to the request/response pairing: Use Throttle::isWithinTimespanMs for the outstanding-request expiry instead of comparing millis()/1000 sums, which mis-expired across the millis() rollover. Bind each accepted response to a request type actually sent to that node. Each outstanding record now carries a bitmask of the response variants its requests authorize, so a get_owner request no longer admits a get_module_config response. The mask accumulates per remote, so a client may still pipeline several request types to one node and have every answer accepted.
|
Both addressed in 21a8f96. millis() rollover: the expiry now uses Response-type binding: each outstanding record now carries a bitmask of the response variants its requests authorize ( |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/modules/AdminModule.cpp (1)
1894-1911: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winFix rollover vulnerability in oldest-slot eviction and prevent resurrection of expired requests.
There are two logic issues in the slot allocation:
- Rollover hazard: As per coding guidelines, raw timestamp comparisons like
o.sentAtMs < slot->sentAtMsare vulnerable tomillis()rollover. Whenmillis()wraps, newer timestamps appear smaller than older pre-wrap timestamps, causing the newest pending request to be evicted instead of the oldest. Compare elapsed time instead.- Resurrection of expired requests: If an existing slot for the node is found but has already expired,
sameNoderemains true. This ORs the new request bit with the expiredexpectedResponsesmask, resetting the 5-minute timer and unintentionally resurrecting previously expired request types.♻️ Proposed fix
- // Reuse this remote's slot if it has one, else a free slot, else the oldest. - OutstandingAdminRequest *slot = nullptr; - for (auto &o : outstandingAdminRequests) - if (o.to == p.to) - slot = &o; - const bool sameNode = slot != nullptr; - if (!slot) - for (auto &o : outstandingAdminRequests) - if (o.to == 0) { - slot = &o; - break; - } - if (!slot) { - slot = &outstandingAdminRequests[0]; - for (auto &o : outstandingAdminRequests) - if (o.sentAtMs < slot->sentAtMs) - slot = &o; - } + // Reuse this remote's slot if it has one, else a free slot, else the oldest. + OutstandingAdminRequest *slot = nullptr; + for (auto &o : outstandingAdminRequests) { + if (o.to == p.to) { + slot = &o; + break; + } + } + + bool sameNode = slot != nullptr; + if (sameNode && !Throttle::isWithinTimespanMs(slot->sentAtMs, kOutstandingAdminRequestMs)) { + sameNode = false; // Expired; do not inherit stale expected responses + } + + if (!slot) { + for (auto &o : outstandingAdminRequests) { + if (o.to == 0) { + slot = &o; + break; + } + } + } + if (!slot) { + slot = &outstandingAdminRequests[0]; + uint32_t now = millis(); + for (auto &o : outstandingAdminRequests) { + if ((uint32_t)(now - o.sentAtMs) > (uint32_t)(now - slot->sentAtMs)) { + slot = &o; + } + } + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/modules/AdminModule.cpp` around lines 1894 - 1911, Update the slot allocation logic around outstandingAdminRequests to select the oldest slot using rollover-safe elapsed-time comparisons rather than raw sentAtMs ordering. When reusing a matching node slot, detect whether it has expired and clear/reset that slot’s prior request state so sameNode is false for the new request, preventing expired response bits from being resurrected or extending their timer.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/modules/AdminModule.cpp`:
- Around line 1894-1911: Update the slot allocation logic around
outstandingAdminRequests to select the oldest slot using rollover-safe
elapsed-time comparisons rather than raw sentAtMs ordering. When reusing a
matching node slot, detect whether it has expired and clear/reset that slot’s
prior request state so sameNode is false for the new request, preventing expired
response bits from being resurrected or extending their timer.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 0efc028a-9f2e-4bec-a17e-c0c4be8fb11b
📒 Files selected for processing (3)
src/modules/AdminModule.cppsrc/modules/AdminModule.htest/test_admin_session_repro/test_main.cpp
🚧 Files skipped from review as they are similar to previous changes (2)
- src/modules/AdminModule.h
- test/test_admin_session_repro/test_main.cpp
Includes HM330X sensor used at the fab26 event.
|
Definitely approve of the concept |
|
@copilot resolve the merge conflicts in this pull request |
# Conflicts: # .github/workflows/flasher-link-comment.yml # .github/workflows/flasher-link-placeholder.yml # .github/workflows/main_matrix.yml # .github/workflows/stale_bot.yml # .github/workflows/update_protobufs.yml # .trunk/trunk.yaml # platformio.ini # protobufs # src/FSCommon.cpp # src/FSCommon.h # src/Power.cpp # src/detect/LoRaRadioType.h # src/detect/ScanI2C.cpp # src/detect/ScanI2C.h # src/detect/ScanI2CTwoWire.cpp # src/graphics/Screen.cpp # src/graphics/draw/CompassRenderer.cpp # src/graphics/draw/NodeListRenderer.cpp # src/graphics/draw/UIRenderer.cpp # src/mesh/LR11x0Interface.cpp # src/mesh/NodeDB.cpp # src/mesh/PhoneAPI.cpp # src/mesh/PositionPrecision.cpp # src/mesh/PositionPrecision.h # src/mesh/RadioInterface.cpp # src/mesh/RadioInterface.h # src/mesh/RadioLibInterface.cpp # src/mesh/RadioLibInterface.h # src/mesh/generated/meshtastic/atak.pb.h # src/mesh/generated/meshtastic/config.pb.h # src/mesh/generated/meshtastic/mesh.pb.h # src/mesh/generated/meshtastic/telemetry.pb.h # src/modules/StatusLEDModule.cpp # src/modules/WaypointModule.cpp # src/motion/AccelerometerThread.h # src/motion/ICM42607PSensor.cpp # src/motion/ICM42607PSensor.h # src/motion/MMC5983MASensor.cpp # src/motion/MagnetometerThread.h # src/motion/MotionSensor.cpp # src/motion/MotionSensor.h # src/platform/esp32/architecture.h # src/platform/esp32/main-esp32.cpp # src/platform/portduino/SimRadio.cpp # src/power/SGM41562.h # test/test_position_precision/test_main.cpp # test/test_radio/test_main.cpp # variants/esp32/chatter2/platformio.ini # variants/esp32/esp32-common.ini # variants/esp32/m5stack_core/platformio.ini # variants/esp32/wiphone/platformio.ini # variants/esp32s3/ELECROW-ThinkNode-G3/platformio.ini # variants/esp32s3/ELECROW-ThinkNode-M7/platformio.ini # variants/esp32s3/heltec_v4/platformio.ini # variants/esp32s3/heltec_v4_r8/platformio.ini # variants/esp32s3/heltec_wireless_tracker/platformio.ini # variants/esp32s3/heltec_wireless_tracker_V1_0/platformio.ini # variants/esp32s3/heltec_wireless_tracker_v2/platformio.ini # variants/esp32s3/mesh-tab/platformio.ini # variants/esp32s3/picomputer-s3/platformio.ini # variants/esp32s3/rak_wismesh_tap_v2/platformio.ini # variants/esp32s3/t-deck/platformio.ini # variants/esp32s3/t-watch-s3/platformio.ini # variants/esp32s3/tlora-pager/platformio.ini # variants/esp32s3/tracksenger/platformio.ini # variants/esp32s3/unphone/platformio.ini # variants/native/portduino.ini # variants/nrf52840/heltec_mesh_node_t1/platformio.ini # variants/nrf52840/heltec_mesh_node_t1/variant.h # variants/nrf52840/t-impulse-plus/variant.h # version.properties
Done — merged |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@bin/config.d/lora-ZebraHatDuo_R1_1W.yaml`:
- Around line 4-6: Correct the Meta.name value in the ZebraHatDuo_R1_1W
configuration from “Radio 0” to “Radio 1,” while leaving the other metadata
unchanged.
In `@variants/esp32s3/ELECROW-ThinkNode-M7/variant.cpp`:
- Around line 12-18: Remove the duplicate initVariant definition in
variants/esp32s3/ELECROW-ThinkNode-M7/variant.cpp, retaining the original
implementation. Also remove the duplicate getCurrentRSSI and enableInterrupt
definitions in src/mesh/SX126xInterface.cpp, preserving each original
implementation so every function has a single definition.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: dfb4b76b-a733-4d87-8f26-eeb12c2e84b5
📒 Files selected for processing (14)
.github/workflows/nightly.yml.github/workflows/trunk_annotate_pr.yml.github/workflows/trunk_check.ymlbin/config.d/lora-ZebraHatDuo_R0_1W.yamlbin/config.d/lora-ZebraHatDuo_R1_1W.yamlbin/org.meshtastic.meshtasticd.metainfo.xmldebian/changelogsrc/detect/ScanI2CTwoWire.hsrc/graphics/draw/CompassRenderer.hsrc/mesh/SX126xInterface.cppsrc/platform/nrf52/NRF52Bluetooth.cppsrc/platform/nrf52/NRF52Bluetooth.hvariants/esp32s3/ELECROW-ThinkNode-M7/variant.cppvariants/esp32s3/m5stack_cardputer_adv/variant.h
💤 Files with no reviewable changes (1)
- src/graphics/draw/CompassRenderer.h
|
@copilot resolve the merge conflicts in this pull request |
Admin
*_responsemessages skip both the sender-authorization branch and thesession-passkey check that gate every other admin message:
The module is bound to no channel, so on any channel it can decode, a node can deliver
a
get_module_config_responseit never asked for.handleGetModuleConfigResponsethenwrites the responder's node_num and pin list into
devicestate.node_remote_hardware_pins.Accept a response only from a node we sent the matching request to.
MeshService::handleToRadionow records the destination of each outgoing admin request (per remote, with the pinned PKC key
when the client set one), and the response branch checks for a live matching request within the
same window as the session passkey. Local admin (
from == 0) is unchanged - PhoneAPI alreadygates that connection.
Second, smaller fix in the same path: the response dispatch compared
get_module_config_response.which_payload_variant(a ModuleConfig oneof tag,remote_hardware= 9)against
meshtastic_AdminMessage_ModuleConfigType_REMOTEHARDWARE_CONFIG(an AdminMessage enum, = 8).They never matched, so the handler was unreachable. It's compared against the oneof tag now, the
same way the request-side switch already builds the response.
Tests (test_admin_session_repro): the pairing gate directly - unsolicited response rejected,
solicited accepted, a request to one node does not admit another, and a setter does not arm the
gate - plus two end-to-end cases through handleReceivedProtobuf asserting the pin table is left
alone for an unsolicited response and updated for a solicited one (the latter also covering the
dispatch fix).
Note: touches AdminModule alongside the in-flight signing series (#10967, #10969, #10972); no
overlap on the response path, but they will need a trivial rebase around the switch.
Summary by CodeRabbit
Bug Fixes
Tests
New Hardware Support