You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
F-21: Use-after-free when returning to the main screen #24
Severity: HIGH · Effort: Medium · Batch: 1 · Status: Code done in #21 (7c980f9), awaiting bench verification
Found in the second full review of the firmware. Recorded with the rest of the pass (F-19…F-44) in docs/REVIEW_REMEDIATION_PLAN_2.md, which is where its status is kept.
Description
ThrottleController::updateUI reads the UI callback and its userData without a lock, and MainScreen::onUIUpdateNeeded then blocks in lvgl_port_lock(200). If the LVGL task is running
a Back handler at that moment, AppController::showMainScreen() destroys the old MainScreen
and builds a new one while holding the LVGL lock. Deregistering in the destructor is too late:
the encoder or network task already holds the old pointer, and when the lock frees it repaints
freed memory — heap corruption or a reboot with trains running. PowerStatusBar's track-power
callback has the same shape.
Plan
Stop destroying the main screen. AppController builds one MainScreen for the life of the
application and showMainScreen() re-shows it (lv_scr_load plus a repaint). A screen that is
never freed cannot be used after it is freed, and the fix also closes F-32.
That makes MainScreen::create a one-off, which exposes a second problem: it overwrote the
WiThrottle client's single connection-state slot, which the active backend owns and the knob
gating depends on, and JmriConfigScreen then nulled that slot on Back. Once the main screen is
no longer rebuilt on return, nothing would put it back. So:
MainScreen no longer registers on WiThrottleClient at all: the backend's own callback
already reaches updateUI, which repaints the main screen.
JmriConfigScreen polls its status on an LVGL timer, as SettingsScreen and OrchestratorConfigScreen already do, instead of taking either client's slot.
MainScreen is constructed once per boot; MainScreen::create now takes only the
controller.
No UI class calls setConnectionStateCallback on a client.
Knob gating still follows the WiThrottle link after visiting the JMRI screen and
returning.
Bench: navigate Settings → Back twenty times while spinning a knob, on a build with CONFIG_HEAP_POISONING_COMPREHENSIVE; no crash, no heap-corruption report.
Activity