fix(mqtt): scan every slot for a stale JWT after a clock correction

The post-NTP-correction refresh looped to _max_active_slots, which is a count of
activation positions and never an index bound. The indices holding those
positions are not contiguous: a slot passed over by isSlotReady() -- or, since
the demand-driven work, by a failed setup -- leaves a higher index activated. On
a two-position board that meant slots 1 and 2 could be live while only indices 0
and 1 were scanned, so slot 3 kept a JWT issued against the pre-correction clock
until its own expiry or a reconnect regenerated it.

Now bounded by RUNTIME_MQTT_SLOTS. The existing guard already skips disabled,
non-JWT, and clientless slots, so widening the range cannot touch a slot that
was never set up.

Pre-existing (the loop predates the demand-driven work) and kept as its own
commit so it can be picked separately. Audited the other _max_active_slots uses:
all are count comparisons or log arguments, so this was the only misuse.
This commit is contained in:
agessaman
2026-08-04 14:04:39 -07:00
parent 53bae5684e
commit dbdf2d732f
+7 -1
View File
@@ -3925,7 +3925,13 @@ bool MQTTBridge::syncTimeWithNTP(bool force, bool primary_only) {
// and re-setup all JWT-authenticated slots so they get fresh tokens.
if (_slots_setup_done && was_ntp_synced) {
unsigned long current_time = (unsigned long)time(nullptr);
for (int i = 0; i < _max_active_slots; i++) {
// Every slot, not _max_active_slots: that is a count of positions, never an
// index bound. Which indices hold those positions is not contiguous — a slot can
// fail isSlotReady() or its setup and be passed over, leaving a higher index
// activated — so bounding by the cap silently skipped an activated slot and left
// it holding a JWT issued against the pre-correction clock. The guard below
// already excludes disabled, non-JWT, and clientless slots.
for (int i = 0; i < RUNTIME_MQTT_SLOTS; i++) {
bool slot_jwt = (_slots[i].preset && _slots[i].preset->auth_type == MQTT_AUTH_JWT) ||
(!_slots[i].preset && _slots[i].audience[0] != '\0');
if (_slots[i].enabled && slot_jwt && _slots[i].client) {