On 30/08/2026 at 07:05:51 +08, Kaiwen Shi <[email protected]> wrote:

> local->assoc_dev is shared between the association path and the
> association-response worker without common synchronization.
>
> mac802154_perform_association() stores the coordinator pointer and waits
> for a response. Its timeout and error paths clear the pointer and return
> to mac802154_associate(), which may then free the coordinator object.
> Meanwhile, mac802154_rx_mac_cmd_worker() may observe the associating bit
> and enter mac802154_process_association_resp(), which dereferences
> assoc_dev.
>
> The worker's bit test and the handler's pointer dereference are not
> atomic with respect to cleanup. Cleanup can clear assoc_dev between them,
> causing a NULL dereference, or free the coordinator while the response
> handler still uses the pointer.
>
> The recorded result is exposed to the same window. assoc_status and
> assoc_addr are written by the handler but read by the association path
> while the associating bit is still set, so a second response for the same
> request - a malicious one, for instance - can replace them between those
> reads and leave the caller with an incoherent status and address pair.
>
> The response handler only needs the coordinator extended address.
> Replace assoc_dev with a cached address, removing the pointer lifetime
> dependency. Protect the cached address and the associating bit with a
> dedicated spinlock. A READ_ONCE()/WRITE_ONCE() pair would not guarantee
> an atomic __le64 access on all 32-bit architectures.
>
> wpan_dev->association_lock cannot be reused here: nl802154_associate()
> holds it across rdev_associate(), hence for the whole of
> mac802154_perform_association() including the wait for the response.
> A response handler taking that lock would only get it once the
> association has already given up.
>
> Reset the completion, publish the cached address, and set the associating
> bit while holding the lock. The response handler takes the lock, rechecks
> the bit and the cached address, records the response, clears the bit, and
> only then completes the waiter. Thus cleanup cannot pass the handler
> between its state check and completion, and the cached 64-bit value
> cannot tear.
>
> The handler clears the bit before completing, not the woken waiter:
> otherwise complete() is issued under the lock and a second (e.g.
> malicious) response can reacquire it before the waiter and replace the
> result. So a wait that returns success implies the bit is already clear,
> and the success and negative-response paths return directly. The
> transmit-error and timeout paths still clear it under assoc_lock, which
> serializes any racing response against the cleanup while the call returns
> the error it already selected. Both paths snapshot assoc_status and
> assoc_addr under the same lock.
>
> Both users run in process context, so a plain spinlock is sufficient.
> The lock is not held while waiting for the completion.
>
> Suggested-by: Miquel Raynal <[email protected]>
> Suggested-by: Xuanqiang Luo <[email protected]>
> Fixes: fefd19807fe9 ("mac802154: Handle associating")
> Cc: [email protected]
> Signed-off-by: Kaiwen Shi <[email protected]>
> ---

Reviewed-by: Miquel Raynal <[email protected]>

Thanks,
Miquèl

Reply via email to