In `kcm_sendmsg()`, every TX fragment is charged to both `sk->sk_wmem_queued` and `sk->sk_forward_alloc` (`sk_mem_charge()`) via `skb_copy_to_page_nocache()` or `MSG_SPLICE_PAGES`. However, the assembled `head` skb is queued onto `&sk->sk_write_queue` (or held in `kcm->seq_skb`) without `head->sk` or a destructor set. Consequently: 1. When `kcm_write_msgs()` finishes transmitting `head`, it subtracts `txm->sent` from `sk->sk_wmem_queued` but never calls `sk_mem_uncharge(sk, txm->sent)`. 2. When `kcm_sendmsg()` fails under `out_error`, or when `kcm_release()` frees `kcm->seq_skb` and purges `&sk->sk_write_queue`, `kfree_skb()` is called without decrementing `sk->sk_wmem_queued` or calling `sk_mem_uncharge()`, triggering a `WARN_ON` in `inet_sock_destruct()` and `__sk_destruct()` when the KCM socket is closed. Use `sk_wmem_queued_add(sk, -...)` and `sk_mem_uncharge(sk, ...)` whenever freeing TX skbs in `kcm_write_msgs()`, `kcm_sendmsg()`, and `kcm_release()`. Fixes: ab7ac4eb9832 ("kcm: Kernel Connection Multiplexor module") Assisted-by: LLM Signed-off-by: Hui Peng --- net/kcm/kcmsock.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c index 71af69d442f2..accf0d427e11 100644 --- a/net/kcm/kcmsock.c +++ b/net/kcm/kcmsock.c @@ -693,7 +693,8 @@ static int kcm_write_msgs(struct kcm_sock *kcm) } /* Successfully sent the whole packet, account for it. */ - sk->sk_wmem_queued -= txm->sent; + sk_wmem_queued_add(sk, -txm->sent); + sk_mem_uncharge(sk, txm->sent); total_sent += txm->sent; skb_dequeue(&sk->sk_write_queue); kfree_skb(head); @@ -960,10 +961,17 @@ static int kcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) */ if (copied) goto partial_message; - if (head != kcm->seq_skb) + if (head && head != kcm->seq_skb) { + sk_wmem_queued_add(sk, -head->len); + sk_mem_uncharge(sk, head->len); kfree_skb(head); + } } else { - kfree_skb(head); + if (head) { + sk_wmem_queued_add(sk, -head->len); + sk_mem_uncharge(sk, head->len); + kfree_skb(head); + } kcm->seq_skb = NULL; } @@ -1686,6 +1694,7 @@ static int kcm_release(struct socket *sock) struct kcm_sock *kcm; struct kcm_mux *mux; struct kcm_psock *psock; + struct sk_buff *skb; if (!sk) return 0; @@ -1695,13 +1704,21 @@ static int kcm_release(struct socket *sock) lock_sock(sk); sock_orphan(sk); - kfree_skb(kcm->seq_skb); + if (kcm->seq_skb) { + sk_wmem_queued_add(sk, -kcm->seq_skb->len); + sk_mem_uncharge(sk, kcm->seq_skb->len); + kfree_skb(kcm->seq_skb); + } /* Purge queue under lock to avoid race condition with tx_work trying * to act when queue is nonempty. If tx_work runs after this point * it will just return. */ - __skb_queue_purge(&sk->sk_write_queue); + while ((skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) { + sk_wmem_queued_add(sk, -skb->len); + sk_mem_uncharge(sk, skb->len); + kfree_skb(skb); + } release_sock(sk); -- 2.55.0.1082.g2b9226bbc0-goog