fix(screen): harden mirror from adversarial review

Gate frame delivery on getAdminAuthorized under access control (screen
pixels are operator content, same rule as mesh packets); move the drain
to lowest getFromRadio/available priority behind the replay drain; give
each PhoneAPI its own (frameId, offset) cursor via ScreenMirror::copyChunk
so coexisting BLE/serial/TCP clients each receive complete frames; disarm
and free the snapshot on client close and on setMirror(false), with
memaudit accounting and a geometry-change realloc guard; honor the
local-connection-only contract for both admin verbs (mp.from == 0);
demote the per-frame onNotify log to TRACE; hoist the guard into a
derived HAS_SCREEN_MIRROR in configuration.h.

Regenerates protos for the reviewed contract (FORMAT_UNSPECIFIED = 0,
MONO_VLSB = 1, width/height int_size:16).
This commit is contained in:
James Rich committed 2026-11-03 07:39:17 -06:00
1 parent 74bf36e3d1
commit 270bf00f6c
7 files changed
+120 -47

No files matched your search

+8 -1
View File
@@ -696,13 +696,20 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
handleSendInputEvent(r->send_input_event);
break;
}
#if HAS_SCREEN && !defined(MESHTASTIC_EXCLUDE_SCREEN_MIRROR)
#if HAS_SCREEN_MIRROR
// Both verbs are documented local-connection-only: frames ride FromRadio,
// which never crosses the mesh, so honoring a remote arm request would
// stream the screen to whatever local client happens to be attached.
case meshtastic_AdminMessage_get_display_frame_request_tag: {
if (mp.from != 0)
break;
LOG_INFO("Client requests display frame");
graphics::screenMirror.requestFrame();
break;
}
case meshtastic_AdminMessage_set_display_mirror_tag: {
if (mp.from != 0)
break;
LOG_INFO("Client sets display mirror: %d", r->set_display_mirror);
graphics::screenMirror.setMirror(r->set_display_mirror);
break;