mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-07-26 23:08:11 +00:00
feat(diag): reset-counters popup; harden repeater loop-detect bounds
- DiagnosticsScreen: Enter opens a confirm popup (defaults to Cancel) that zeroes the cumulative counters via Mesh::resetStats(). - Dispatcher::resetStats() made virtual; Mesh overrides it to also clear n_forwarded so the reset covers the repeater forward count. - REPEAT_LOOP_MAX gains a 4-byte-hash entry and isRepeatLooped() bounds- checks hash_size, so it can't OOB-read if path_mode 3 is ever accepted. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -525,7 +525,11 @@ bool MyMesh::filterRecvFloodPacket(mesh::Packet* packet) {
|
||||
// thresholds (max times this node's hash may already appear in the path, by hash size).
|
||||
// A companion moves around, so it re-enters its own flood's path more easily than a
|
||||
// fixed repeater — hardcoded rather than configurable since there's no CLI here.
|
||||
static const uint8_t REPEAT_LOOP_MAX[] = { 0, /*1-byte*/ 2, /*2-byte*/ 1, /*3-byte*/ 1 };
|
||||
// Indexed by getPathHashSize() = (path_len>>6)+1, so 1..4. Index 0 is unused
|
||||
// (hash size is never 0); index 4 covers path_mode 3, which tryParsePacket
|
||||
// currently rejects — kept in-bounds so this can't OOB-read if that guard is
|
||||
// ever relaxed.
|
||||
static const uint8_t REPEAT_LOOP_MAX[] = { 0, /*1-byte*/ 2, /*2-byte*/ 1, /*3-byte*/ 1, /*4-byte*/ 1 };
|
||||
// Caps how many hops an ADVERT flood gets repeated, matching simple_repeater's default
|
||||
// flood_max_advert — adverts are the most frequent flood traffic, so this is the one
|
||||
// depth limit worth keeping even without the rest of simple_repeater's flood_max knobs.
|
||||
@@ -533,6 +537,7 @@ static const uint8_t REPEAT_MAX_ADVERT_HOPS = 8;
|
||||
|
||||
bool MyMesh::isRepeatLooped(const mesh::Packet* packet) const {
|
||||
uint8_t hash_size = packet->getPathHashSize();
|
||||
if (hash_size >= sizeof(REPEAT_LOOP_MAX)) return true; // unknown hash size: treat as looped, don't forward
|
||||
uint8_t hash_count = packet->getPathHashCount();
|
||||
uint8_t n = 0;
|
||||
const uint8_t* path = packet->path;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <helpers/DeviceDiag.h>
|
||||
#include <Arduino.h>
|
||||
#include "icons.h"
|
||||
#include "PopupMenu.h"
|
||||
#include "../MyMesh.h"
|
||||
|
||||
extern MyMesh the_mesh;
|
||||
@@ -18,6 +19,7 @@ extern MyMesh the_mesh;
|
||||
class DiagnosticsScreen : public UIScreen {
|
||||
UITask* _task;
|
||||
int _scroll = 0;
|
||||
PopupMenu _reset_menu; // Enter → confirm before zeroing the cumulative counters
|
||||
|
||||
struct Row { const char* label; char value[20]; };
|
||||
static const int MAX_ROWS = 13;
|
||||
@@ -128,13 +130,31 @@ public:
|
||||
}
|
||||
drawScrollIndicator(display, start_y, visible * item_h, _row_count, visible, _scroll);
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
return 1000; // stats refresh once a second — no need for a faster redraw
|
||||
if (_reset_menu.active) _reset_menu.render(display);
|
||||
return _reset_menu.active ? 50 : 1000; // popup wants snappier redraw; stats otherwise refresh once a second
|
||||
}
|
||||
|
||||
bool handleInput(char c) override {
|
||||
if (_reset_menu.active) {
|
||||
auto res = _reset_menu.handleInput(c);
|
||||
// Index 0 = "Reset", 1 = "Cancel" (destructive option first, but never the
|
||||
// default landing row — see setSelected below).
|
||||
if (res == PopupMenu::SELECTED && _reset_menu.selectedIndex() == 0) {
|
||||
the_mesh.resetStats(); // zeroes Dispatcher per-type counters + Mesh forward count
|
||||
_task->showAlert("Counters reset", 800);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (c == KEY_UP) { if (_scroll > 0) _scroll--; return true; }
|
||||
if (c == KEY_DOWN) { _scroll++; return true; } // clamped to max_scroll in render()
|
||||
if (c == KEY_CANCEL || c == KEY_CONTEXT_MENU || c == KEY_ENTER) { _task->gotoToolsScreen(); return true; }
|
||||
if (c == KEY_ENTER) {
|
||||
_reset_menu.begin("Reset counters?", 2);
|
||||
_reset_menu.addItem("Reset");
|
||||
_reset_menu.addItem("Cancel");
|
||||
_reset_menu.setSelected(1); // default to Cancel so a stray Enter doesn't wipe stats
|
||||
return true;
|
||||
}
|
||||
if (c == KEY_CANCEL || c == KEY_CONTEXT_MENU) { _task->gotoToolsScreen(); return true; }
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user