Skip to content

datastorage: do not reset rcpi/rsni on expiry - #276

Merged
PolynomialDivision merged 1 commit into
berlin-open-wireless-lab:masterfrom
micpf:fix/rcpi-rsni-reset-race
Aug 10, 2026
Merged

datastorage: do not reset rcpi/rsni on expiry#276
PolynomialDivision merged 1 commit into
berlin-open-wireless-lab:masterfrom
micpf:fix/rcpi-rsni-reset-race

Conversation

@micpf

@micpf micpf commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

remove_old_probe_entries() marks entries as rcpi_expired when their rcpi_timestamp is older than the threshold, and — before doing so — overwrites rcpi and rsni with -1.

The overwrite is unsafe: eval_probe_metric() reads probe_entry->rcpi on the scoring path (src/storage/datastorage.c around line 215):

if (!signal_available && probe_entry->rcpi <= 220) {
    signal = rcpi_to_rssi(probe_entry->rcpi);
    ...
}

If the expiry pass writes rcpi = -1 while a scorer is between the range check and the rcpi_to_rssi() call — or afterwards, when the scorer picks up the stale-then--1 value — rcpi_to_rssi() gets a bogus argument and the score is silently corrupted.

The overwrite is also unnecessary for the reap itself: entries whose rcpi_timestamp has aged out are already tracked via rcpi_expired, and once both rcpi_expired and rssi_expired are set the entry is unlinked and freed a few lines below. Incoming beacon reports overwrite rcpi/rsni unconditionally, so no observer depends on the -1 sentinel.

This drops the two writes and relies solely on rcpi_expired to drive the reap.

remove_old_probe_entries() runs periodically and, when a probe entry's
rcpi_timestamp is older than the threshold, marks the entry rcpi_expired
so it can be reaped once the RSSI side has also expired.

Before the reap decision it also overwrites rcpi and rsni with -1.
That mutation is only safe if nothing else is reading those fields at
the same time, but eval_probe_metric() -- called from the scoring path
under a different lock -- does exactly that:

    if (!signal_available && probe_entry->rcpi <= 220) {
        signal = rcpi_to_rssi(probe_entry->rcpi);
        ...
    }

If the expiry pass writes rcpi = -1 between the range check and the
subsequent read (or a later reader takes the "valid RCPI" branch based
on a still-valid rcpi and then observes -1 after the -1 store becomes
visible), rcpi_to_rssi() is fed a bogus value and the score for that
probe is silently corrupted.

The -1 write is also unnecessary for the reap itself: entries whose
rcpi_timestamp has aged out are already tracked via rcpi_expired, and
once both rcpi_expired and rssi_expired are set the entry is unlinked
and freed a few lines below. Fresh beacon reports overwrite rcpi/rsni
directly, so there is no observer that requires the -1 sentinel.

Drop the writes and rely solely on rcpi_expired to drive the reap.

Signed-off-by: Michael Pfeifroth <micpf@westermo.com>
@PolynomialDivision
PolynomialDivision merged commit a11dd33 into berlin-open-wireless-lab:master Aug 10, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants