From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=100.103.45.18 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1794524174; b=FZnbd4oZE90OY72p7dEwXbyaHXhHc4KxuzXxh6smqSH8Vg3G/qur+4gdDNqaLpv8AigT h6dNco/6xOIElnY/omgvA3xoenZ8j3Y3grTxNo94iGILm1sRP7KjQsq3RRgZ5965an83e 8q4r3yUe2JB7IV2wsk61fjbMaS99x0WY9mi96kNfKVoQc9+2Eq25w7X9bgdHkyhqRvX1o ZBZOT1U+UIePhEUrKM0wiF6rHOlDS2LkWm8sIE9xlU78yP7lP7f2iWK45FUqRYwIhWcaR ralntx83PwEROLCSchpMeLzpecNirh/ceLF8sVodxRYSgOGK8KV/17u28F/xo6zrV+w== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1794524174; h=Received:DKIM-Signature:From:Date:Subject:MIME-Version:Content-Type: Content-Transfer-Encoding:Message-Id:References:In-Reply-To:To:Cc: X-Mailer:X-Developer-Signature:X-Developer-Key; bh=7KXZct2Nh4QdUFm8d2XuDz0d/faDhoWyH6HWizHXA2E=; b=q9+L+6dvpoxk+bXk5vsuHHtfEhNQUVmm3lNLBDtiBWL0UMc4Jr+n+5DEjF3cjKtiZC+c YO/wwwzNf0a07XI7K/OPnK/8cQSJxABtNtbRgLrBZZPbqbU3y8ZjoBMahJVc/X2s/2cTo L2YwvQcn3byxVg0bD48M2gZx+foaMzyGzU8OT6rNd/yzXYTLxRq5ud0lHXncCFSZdNPUC szJ9MU79MtjqJzOymCkEj47kHbMLef+Y2GQNM1Dodk/T5jGWRfuvkFVmXF0GHMbbP/bxz iSl0W7U1jcF5GO6rDdxCBgUdI+S06ZBKCEs/ZozCh0zukBZPrrV52+gdqAOrATZTg3w== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b="lpa/UJ09"; arc=none smtp.remote-ip=100.103.45.18 Received: from smtp.kernel.org (quasi.space.kernel.org [100.103.45.18]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 63DCF60A262C; Thu, 12 Nov 2026 22:56:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b=lpa/UJ09 Received: by smtp.kernel.org (Postfix) with ESMTPSA id D17F61F000C9; Thu, 12 Nov 2026 22:55:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1794524173; bh=7KXZct2Nh4QdUFm8d2XuDz0d/faDhoWyH6HWizHXA2E=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=lpa/UJ09XlXh3NXrGdzLl3Y0yzxu/o2Bzw5r3haCfnWuH7X8CHkF8yj3NsQz0KkSj vONam95AbPdOBj9C6SkI6dRw7H9KgGH6g6wEXqWeftz8Nk2pZJmw1Jk12PbpEiImdc 2B6PIpgLL3gF5jGr/Gxw8yPAyb+TIkLNV5lE3JggBkwitvGWL2MMQQR+flkvvzRiRU rMu2dX7Wq6wtwpn2mK6E9ZBK0ZUH4JI2fHrxZhPneJVYInZniV34L+vIM3a41tZcsa P+jT5nwYN8FQ41gjR9R1A427bI9HACxtMDz4ugRU9nN4n5Yd+pem/niRiHkliLrF65 /7WwZU/xEuh6g== From: "David Hildenbrand (Arm)" Date: Thu, 12 Nov 2026 23:53:28 +0100 Subject: [PATCH v4 9/9] net: limit folio refcount when splicing from sockets X-Mailing-List: netdev@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20261112-refcount_overflow_stable-v4-9-ad3deef0bd23@kernel.org> References: <20261112-refcount_overflow_stable-v4-0-ad3deef0bd23@kernel.org> In-Reply-To: <20261112-refcount_overflow_stable-v4-0-ad3deef0bd23@kernel.org> To: Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Mike Rapoport , Suren Baghdasaryan , Michal Hocko , "Matthew Wilcox (Oracle)" , Jan Kara , Andrew Morton , Zi Yan , Baolin Wang , Nico Pache , Ryan Roberts , Dev Jain , Barry Song , Lance Yang , Usama Arif , Kiryl Shutsemau , Matthew Brost , Joshua Hahn , Byungchul Park , Gregory Price , Ying Huang , Alistair Popple , Peter Xu , Johannes Weiner , Song Liu , Rik van Riel , Miklos Szeredi , Alexander Viro , Christian Brauner , Hugh Dickins , Will Deacon , "Aneesh Kumar K.V" , Nick Piggin , Peter Zijlstra , Arnd Bergmann , Jason Gunthorpe , Kevin Tian , "Joerg Roedel (AMD)" , Robin Murphy , Steve Sistare , John Hubbard , "Jose A. Perez de Azpillaga" , Josef Bacik , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Neal Cardwell , Kuniyuki Iwashima Cc: "David Hildenbrand (Arm)" , linux-mm@kvack.org, linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, fuse-devel@lists.linux.dev, linux-arch@vger.kernel.org, iommu@lists.linux.dev, netdev@vger.kernel.org, stable@vger.kernel.org, Pedro Falcato X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=7665; i=david@kernel.org; h=from:subject:message-id; bh=YprwJxPujKbTqAJR7+MEgkU1slwl4fvrcUXKr6083QM=; b=owGbwMvMwCF2/OVpCdfQ3ZWMp9WSGLK+OWdZhIuraRTZhSu/umUgt+rJTueTAgtSJm4s/rjwj O+luT6mHaUsDGIcDLJiiixBwfcNXr5cV1vtxscMM4eVCWQIAxenAEyEJZ3hn/lfbe1bRUyliy9c uL1I+nC+bFJz4fp76hO89m+oK5yxbCMjw8oYEdvXjHOUMgT4Y5sW1N467Bi75R2f1eEXcVeX3Gr 35QEA X-Developer-Key: i=david@kernel.org; a=openpgp; fpr=1BD9CAAD735C4C3A460DFCCA4DDE10F700FF835A Various networking features support splicing data from sockets into pipes (TCP, AF_UNIX, KCM, kTLS), whereby spd_fill_page() takes an additional page reference for each fragment, while the skb keeps its own reference until it was fully consumed. This allows for exceeding folio refcount limits. This is not specific to large pagecache folios, but with them it is a lot easier to reach the refcount limit, as page table mappings can cheaply create many references. Let's use try_get_page() in spd_fill_page() and stop splicing with -EFAULT if that fails. To propagate the error, let spd_fill_page(), __splice_segment() and __skb_splice_bits() return an int instead of a bool. Keep returning true/false instead of 1/0 to reduce the churn in this fix. If we could not splice anything, return the error from skb_splice_bits(): returning 0 would make the callers (TCP, AF_UNIX, KCM, kTLS) report EOF, although data is still queued. The callers already handle errors. Use -EFAULT, consistent with splice_folio_into_pipe(). Fixes: 800d8c63b2e9 ("shmem: add huge pages support") Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS") Fixes: 793917d997df ("mm/readahead: Add large folio readahead") Cc: stable@vger.kernel.org Acked-by: Pedro Falcato Signed-off-by: David Hildenbrand (Arm) --- net/core/skbuff.c | 90 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 51 insertions(+), 39 deletions(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 5e3f910eeea0b..a5965f8fa1b00 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -3150,11 +3150,13 @@ static bool spd_can_coalesce(const struct splice_pipe_desc *spd, } /* - * Fill page/offset/length into spd, if it can hold more pages. + * Fill page/offset/length into spd, if it can hold more pages and we can + * take a page reference. Returns non-zero to stop, or a negative error + * if we cannot take a page reference. */ -static bool spd_fill_page(struct splice_pipe_desc *spd, struct page *page, - unsigned int *len, unsigned int offset, bool linear, - struct sock *sk) +static int spd_fill_page(struct splice_pipe_desc *spd, struct page *page, + unsigned int *len, unsigned int offset, bool linear, + struct sock *sk) { if (unlikely(spd->nr_pages == MAX_SKB_FRAGS)) return true; @@ -3168,7 +3170,8 @@ static bool spd_fill_page(struct splice_pipe_desc *spd, struct page *page, spd->partial[spd->nr_pages - 1].len += *len; return false; } - get_page(page); + if (!try_get_page(page)) + return -EFAULT; spd->pages[spd->nr_pages] = page; spd->partial[spd->nr_pages].len = *len; spd->partial[spd->nr_pages].offset = offset; @@ -3177,12 +3180,14 @@ static bool spd_fill_page(struct splice_pipe_desc *spd, struct page *page, return false; } -static bool __splice_segment(struct page *page, unsigned int poff, - unsigned int plen, unsigned int *off, - unsigned int *len, - struct splice_pipe_desc *spd, bool linear, - struct sock *sk) +static int __splice_segment(struct page *page, unsigned int poff, + unsigned int plen, unsigned int *off, + unsigned int *len, + struct splice_pipe_desc *spd, bool linear, + struct sock *sk) { + int ret; + if (!*len) return true; @@ -3200,8 +3205,9 @@ static bool __splice_segment(struct page *page, unsigned int poff, do { unsigned int flen = min(*len, plen); - if (spd_fill_page(spd, page, &flen, poff, linear, sk)) - return true; + ret = spd_fill_page(spd, page, &flen, poff, linear, sk); + if (ret) + return ret; poff += flen; plen -= flen; *len -= flen; @@ -3213,28 +3219,30 @@ static bool __splice_segment(struct page *page, unsigned int poff, } /* - * Map linear and fragment data from the skb to spd. It reports true if the - * pipe is full or if we already spliced the requested length. + * Map linear and fragment data from the skb to spd. It returns non-zero to + * stop: true if the pipe is full or if we already spliced the requested + * length, or a negative error if we cannot take a page reference. */ -static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe, - unsigned int *offset, unsigned int *len, - struct splice_pipe_desc *spd, struct sock *sk) +static int __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe, + unsigned int *offset, unsigned int *len, + struct splice_pipe_desc *spd, struct sock *sk) { struct sk_buff *iter; - int seg; + int seg, ret; /* map the linear part : * If skb->head_frag is set, this 'linear' part is backed by a * fragment, and if the head is not shared with any clones then * we can avoid a copy since we own the head portion of this page. */ - if (__splice_segment(virt_to_page(skb->data), - (unsigned long) skb->data & (PAGE_SIZE - 1), - skb_headlen(skb), - offset, len, spd, - skb_head_is_locked(skb), - sk)) - return true; + ret = __splice_segment(virt_to_page(skb->data), + (unsigned long)skb->data & (PAGE_SIZE - 1), + skb_headlen(skb), + offset, len, spd, + skb_head_is_locked(skb), + sk); + if (ret) + return ret; /* * then map the fragments @@ -3248,10 +3256,11 @@ static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe, if (WARN_ON_ONCE(!skb_frag_page(f))) return false; - if (__splice_segment(skb_frag_page(f), - skb_frag_off(f), skb_frag_size(f), - offset, len, spd, false, sk)) - return true; + ret = __splice_segment(skb_frag_page(f), + skb_frag_off(f), skb_frag_size(f), + offset, len, spd, false, sk); + if (ret) + return ret; } skb_walk_frags(skb, iter) { @@ -3260,11 +3269,12 @@ static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe, continue; } /* __skb_splice_bits() only fails if the output has no room - * left, so no point in going over the frag_list for the error - * case. + * left or on error, so no point in going over the frag_list + * for the error case. */ - if (__skb_splice_bits(iter, pipe, offset, len, spd, sk)) - return true; + ret = __skb_splice_bits(iter, pipe, offset, len, spd, sk); + if (ret) + return ret; } return false; @@ -3272,7 +3282,9 @@ static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe, /* * Map data from the skb to a pipe. Should handle both the linear part, - * the fragments, and the frag list. + * the fragments, and the frag list. Returns the number of bytes spliced, + * or an error if we could not splice anything because we could not take + * a page reference. */ int skb_splice_bits(struct sk_buff *skb, struct sock *sk, unsigned int offset, struct pipe_inode_info *pipe, unsigned int tlen, @@ -3287,14 +3299,14 @@ int skb_splice_bits(struct sk_buff *skb, struct sock *sk, unsigned int offset, .ops = &nosteal_pipe_buf_ops, .spd_release = sock_spd_release, }; - int ret = 0; + int ret; - __skb_splice_bits(skb, pipe, &offset, &tlen, &spd, sk); + ret = __skb_splice_bits(skb, pipe, &offset, &tlen, &spd, sk); if (spd.nr_pages) - ret = splice_to_pipe(pipe, &spd); - - return ret; + return splice_to_pipe(pipe, &spd); + /* Returning 0 would make callers report EOF. */ + return ret < 0 ? ret : 0; } EXPORT_SYMBOL_GPL(skb_splice_bits); -- 2.56.0