Summary
Devices using TFTView_320x240 enter an infinite reboot loop. On boot,
restoreTextMessages() reads a persisted message with ch=31 from flash
without bounds checking, causing channelGroup[31] out-of-bounds access on a
std::array<lv_obj_t*, 8> → crash → immediate reboot → crash again.
Device & Firmware
| Field |
Value |
| Device |
Heltec V4 TFT (heltec-v4-tft) |
| Firmware |
2.7.21.1370b23 (also on 2.7.20) |
| Crash |
Core 0 panic'ed (StoreProhibited), EXCVADDR 0x00000C20 |
Serial Log
Original firmware crash (StoreProhibited on boot):
[DeviceUI] LogRotate: found 18 log files using 67480 bytes (65%).
[DeviceUI] newMessage: from:0x50edb985, to:0xffffffff, ch:2, ...
... (dozens of valid ch:2 messages restored, then silent crash)
Guru Meditation Error: Core 0 panic'ed (StoreProhibited). Exception was unhandled.
EXCVADDR: 0x00000c20
rst:0xc (RTC_SW_CPU_RST)
Diagnostic build confirming the infinite reboot loop (NVS reboot counter):
Number of Device Reboots: 347
... Guru Meditation Error: Core 0 panic'ed (LoadProhibited). EXCVADDR: 0x0012003e
rst:0xc (RTC_SW_CPU_RST)
Number of Device Reboots: 348
... Guru Meditation Error: Core 0 panic'ed (LoadProhibited). EXCVADDR: 0x0012003e
rst:0xc (RTC_SW_CPU_RST)
Number of Device Reboots: 349
... (continues indefinitely)
Both crashes originate from the same channelGroup[ch=31] OOB — different
exception types reflect different LVGL code paths hit on each build.
Root Cause
restoreTextMessages() [ViewController.cpp:647]
→ restoreMessage(msg.ch=31)
→ newMessageContainer(ch=31)
channelGroup[31] ← OOB on std::array<lv_obj_t*, 8>
→ newMessage(container=NULL)
lv_obj_create(NULL) ← StoreProhibited at 0x00000C20
EXCVADDR = NULL + 0xC20 = lv_obj_t.parent, written by lv_obj_create(NULL).
The ch:31 entry is never printed in the log — crash occurs inside
newMessageContainer(ch=31) before any output.
newMessageContainer, newMessage (6-arg), restoreMessage, and showMessages
have no c_max_channels bounds check — even though the pattern is used in 10+
other places in TFTView_320x240.cpp.
How ch=31 Got Into Flash
Older firmware passed the raw MQTT PSK hash as p.channel without remapping to
slot index. MediumFast default PSK (AQ==) hashes to 0x1f = 31. device-ui
stored this in LittleFS.
Firmware 2.7.21 correctly remaps hash → slot index (confirmed via serial:
"Use channel 0 (hash 0x1f)"), so new messages are stored with ch 0–7. However,
historical ch=31 entries persist across firmware upgrades and crash on every boot.
Any ch >= 8 triggers OOB. With custom PSKs, ~97% of hash values fall outside 0–7.
Workaround
Admin → Reset NodeDB (clears the LittleFS message log).
Notes
I have a fix and will open a PR. May also be related to #68.
Related: meshtastic/firmware#9932
Summary
Devices using
TFTView_320x240enter an infinite reboot loop. On boot,restoreTextMessages()reads a persisted message withch=31from flashwithout bounds checking, causing
channelGroup[31]out-of-bounds access on astd::array<lv_obj_t*, 8>→ crash → immediate reboot → crash again.Device & Firmware
heltec-v4-tft)Core 0 panic'ed (StoreProhibited), EXCVADDR0x00000C20Serial Log
Original firmware crash (StoreProhibited on boot):
Diagnostic build confirming the infinite reboot loop (NVS reboot counter):
Both crashes originate from the same
channelGroup[ch=31]OOB — differentexception types reflect different LVGL code paths hit on each build.
Root Cause
EXCVADDR = NULL + 0xC20=lv_obj_t.parent, written bylv_obj_create(NULL).The ch:31 entry is never printed in the log — crash occurs inside
newMessageContainer(ch=31)before any output.newMessageContainer,newMessage(6-arg),restoreMessage, andshowMessageshave no
c_max_channelsbounds check — even though the pattern is used in 10+other places in
TFTView_320x240.cpp.How ch=31 Got Into Flash
Older firmware passed the raw MQTT PSK hash as
p.channelwithout remapping toslot index. MediumFast default PSK (
AQ==) hashes to0x1f = 31. device-uistored this in LittleFS.
Firmware 2.7.21 correctly remaps hash → slot index (confirmed via serial:
"Use channel 0 (hash 0x1f)"), so new messages are stored with ch 0–7. However,historical ch=31 entries persist across firmware upgrades and crash on every boot.
Any
ch >= 8triggers OOB. With custom PSKs, ~97% of hash values fall outside 0–7.Workaround
Admin → Reset NodeDB (clears the LittleFS message log).
Notes
I have a fix and will open a PR. May also be related to #68.
Related: meshtastic/firmware#9932