From: Ruide Cao <redacted>
SO_RESERVE_MEM updates sk_forward_alloc while holding the socket lock,
whereas UDP receive accounting updates it under sk_receive_queue.lock.
Since sk_forward_alloc_add() is a read-modify-write, concurrent updates
can be lost, leaving socket, protocol, and memcg accounting inconsistent.
Serialize the reservation commit and release with the receive queue lock.
Keep the potentially sleeping precharge outside the lock, while performing
the forward allocation and reserved-memory updates under the same lock used
by UDP receive accounting.
Fixes: 2bb2f5fb21b0 ("net: add new socket option SO_RESERVE_MEM")
Cc: stable@vger.kernel.org
Reported-by: Vega <redacted>
Reported-by: Sashiko
Closes: https://sashiko.dev/#/patchset/20260711005955.1467140-1-xmei5@asu.edu
Assisted-by: LLM
Signed-off-by: Ruide Cao <redacted>
Signed-off-by: Ren Wei <redacted>
---
net/core/sock.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/net/core/sock.c b/net/core/sock.c
index 1ad41904db25..6d35072a2792 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1022,9 +1022,11 @@ static void sock_release_reserved_memory(struct sock *sk, int bytes)
/* Round down bytes to multiple of pages */
bytes = round_down(bytes, PAGE_SIZE);
+ spin_lock_bh(&sk->sk_receive_queue.lock);
WARN_ON(bytes > sk->sk_reserved_mem);
WRITE_ONCE(sk->sk_reserved_mem, sk->sk_reserved_mem - bytes);
sk_mem_reclaim(sk);
+ spin_unlock_bh(&sk->sk_receive_queue.lock);
}
static int sock_reserve_memory(struct sock *sk, int bytes)
@@ -1064,10 +1066,12 @@ static int sock_reserve_memory(struct sock *sk, int bytes)
}
success:
+ spin_lock_bh(&sk->sk_receive_queue.lock);
sk_forward_alloc_add(sk, pages << PAGE_SHIFT);
WRITE_ONCE(sk->sk_reserved_mem,
sk->sk_reserved_mem + (pages << PAGE_SHIFT));
+ spin_unlock_bh(&sk->sk_receive_queue.lock);
return 0;
}
--
2.47.3