Skip to content

[bpf-ci-bot] sk_psock_backlog refcount mismatch causes spurious kernel_splats CI failures #519

Description

@kernel-patches-review-bot

Summary

sk_psock_backlog() uses sk_psock_get(psock->sk) to take a reference before processing queued skbs, but this looks up whatever psock is currently attached to the socket (sk->sk_user_data), which can differ from the psock running the backlog. On the refcount underflow path, this triggers a refcount_warn_saturate WARNING that the new kernel_splats CI check correctly flags, failing otherwise-passing test runs.

Failure Details

Root Cause Analysis

The race exists in net/core/skmsg.c:sk_psock_backlog() (line 693):

if (!sk_psock_get(psock->sk))   /* BUG: may increment WRONG psock's refcnt */
    return;
...
sk_psock_put(psock->sk, psock); /* decrements THIS psock's refcnt */

sk_psock_get(psock->sk) calls sk_psock(sk) which reads rcu_dereference(sk->sk_user_data). If the socket was re-mapped between sk_psock_drop() (which sets sk_user_data = NULL under sk_callback_lock) and sk_psock_stop() (which clears SK_PSOCK_TX_ENABLED AFTER releasing the lock), a new psock can be attached to the socket in that window.

Race timeline:

  1. Old psock (P1) refcount → 0, triggering sk_psock_drop()
  2. sk_psock_drop() sets sk_user_data = NULL (under lock), then releases lock
  3. Another CPU creates new psock P2 for same socket via sk_psock_init() (acquires same lock, sees NULL, creates P2)
  4. P1's backlog fires: SK_PSOCK_TX_ENABLED still set (step 3b sk_psock_stop() hasn't run yet)
  5. sk_psock_get(P1->sk) → finds P2 via sk_user_data → increments P2->refcnt
  6. sk_psock_put(P1->sk, P1) → decrements P1->refcnt (already 0) → UNDERFLOW!

This was introduced by commit 8259eb0e06d8 ("bpf, sockmap: Avoid using sk_socket after free when sending") which added the sk_psock_get(psock->sk) call to synchronize with sock_map_close().

Proposed Fix

Replace sk_psock_get(psock->sk) with refcount_inc_not_zero(&psock->refcnt) to directly increment the backlog's own psock refcount. This:

  • Eliminates the mismatch: always increments/decrements the same psock
  • Preserves the synchronization property: while refcount > 0, sk_psock_drop() hasn't been called, so sk_user_data still points to this psock, meaning sock_map_close()'s sk_psock_get(sk) will find it and call cancel_delayed_work_sync()
  • Correctly fails (returns false) if refcount is already 0, triggering early return

See output/0001-bpf-sockmap-Fix-psock-refcount-mismatch-in-sk_psock_backlog.patch.

Impact

Without the fix, the kernel_splats CI check will intermittently fail sockmap_redir test runs despite all tests passing. This creates misleading CI signal that developers must manually investigate and dismiss. The underlying refcount underflow also indicates a potential use-after-free if the psock is freed while backlog is still processing.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions