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=1794343129; b=dysNl2iuFUJ62VHOmoHE9bd/E/fURIzZzLcFkBsvcK4TNaGsWE0bb+jzyChJ4wKcbJTC ShonGWUWGsisyNqz/8coZ2x/EueZvqbqOgZV4tbTJZBcCiXlzXxERpQp7pd/Iuk2h2Q8i v25ERMlbJGQjZOwZwcuhAkkn1hqxxj+8EfbelULZnF3+rGL2Bqh3vCL4E3eWfXkyYb424 4qzLYu+E67na9gxbmR7hwjZUlyq5V3ihyztViWNzHp1X9geKn6prGlwpF/FKqIEjUsqPK hDH6cj42lXHe4SZBBghkapmblnm7lucCDzaIov1boWEvEoF9EW6mboJzZxXARDxX3AQ== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1794343129; 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=4I3GmTnbTJ+C/rO4Q1xjwO8KlhKDQBrwvfrY+E5Y8ZI=; b=MpFuMzZptF/iNrHuOXpjQMTXHq4OPU4MyGVaCmJDSb236wL7/87Olfz/4DDu4as4xQbg ofMpn3xJPwdVANa1HHKln8kROGXe5t12APFVfoQUmwmpVhKLgam4Z6zSfiMZlLscFYseL CmqKL/0HOrQBF3vuqL/T0TUcPJ1k7QWqSAdN5wCsGwfCMI8ni8TbcW08zvB/h/J4RotX3 O7jCsBvmewq9TUWlAGnIswFKK65Z3pU/aV2++HpbLcFpLOqfhkMYrw1kHu4XkoSoEw2Ub TlJYAjHkYrh5AS+nGZE6cdQvtu2Q+AUb2cj0y7eX+DuSRUR9IFgu4OyFXeAqwQ5xzzQ== 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=aoNQqoKr; 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 BBF31606A326; Tue, 10 Nov 2026 20:38:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b=aoNQqoKr Received: by smtp.kernel.org (Postfix) with ESMTPSA id 372351F008A1; Tue, 10 Nov 2026 20:38:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1794343128; bh=4I3GmTnbTJ+C/rO4Q1xjwO8KlhKDQBrwvfrY+E5Y8ZI=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=aoNQqoKr3Z6Jnx5nX2JhE1cCS211UWveUhHYAD4hLFQwm/1JQNmUAt6V+yPAwkf5z vN9ZIYHWhP8Hpd5/fDaitNXThHErVlsOFI9CuJL6VnXNgYaJFu1LTbvFXbjnVZ8cui WoSilzZ9PEX7YkVJGvRI3Abfj0xQGky0E0GVRLf73dDfjGQU9LZZ8Lvq8L8CifigiX M71WRdl5+jlfNKemDMBCpfDRDbS2lM9NGzRwUSfloiOAbffMd+2RLyDOkmA60HRgIM adk4B0KcRda2tHSroj8DdmHbdA2R4KjoCrpU4aTxX32GbbLMhy4SLAAK4G4UTW85Wr 9n3fhYtN0LeHA== From: "David Hildenbrand (Arm)" Date: Tue, 10 Nov 2026 21:35:47 +0100 Subject: [PATCH v3 7/9] RFC: net: limit folio refcount when rearranging skb frags X-Mailing-List: linux-kernel@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: <20261110-refcount_overflow_stable-v3-7-318625d63a98@kernel.org> References: <20261110-refcount_overflow_stable-v3-0-318625d63a98@kernel.org> In-Reply-To: <20261110-refcount_overflow_stable-v3-0-318625d63a98@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 , Linus Torvalds 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 X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=5031; i=david@kernel.org; h=from:subject:message-id; bh=wbwCYm57JirFvO1jEPagtVrKyKCbDM8zxgcYTSE+rvo=; b=owGbwMvMwCF2QOXvyb15Z08znlZLYsj63BDx9439hIQZPvu3nf67muWokK7svON5d8+dZwzTD s8+6BBi0VHKwiDGwSArpshybV1OavCaVseyKEM3mDmsTCBDGLg4BWAiRyYz/NM3VVycXuvAsLZP 3kxO9c6GuuQV9T4zGn+bVLunaPPwzmL4nxfcW54cODP7sHXsdeFJBxWqeY/1dr6/xuNuXLLrPZc 0PwA= X-Developer-Key: i=david@kernel.org; a=openpgp; fpr=1BD9CAAD735C4C3A460DFCCA4DDE10F700FF835A Various networking features take additional references to frag pages that are still referenced elsewhere when rearranging skb frags, for example software GSO segmentation, TCP receive coalescing of clones, SACK shifting and MTU probing. This is an effective way for multiplying folio references held by an skb, allowing 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 skb_frag_try_ref() and skb_frags_try_ref() in skb_segment(), skb_try_coalesce(), skb_shift() and tcp_clone_payload(). All of these can already fail, and their callers handle that. skb_split() can still add one reference when splitting a frag. But each split also allocates a new skb that is charged to the socket, reaching the refcount limit that way would require a lot of socket memory, and the references go away once the data was acked, so it's not that relevant in practice. Making skb_split() fail is more involved, so let's leave it alone for now. 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 Signed-off-by: David Hildenbrand (Arm) --- net/core/skbuff.c | 32 +++++++++++++++++++++----------- net/ipv4/tcp_output.c | 8 +++++--- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 1d44e9a4152e5..f473c74956b8b 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -4326,7 +4326,8 @@ static int skb_prepare_for_shift(struct sk_buff *skb) * the length of the skb, from skb to tgt. Returns number bytes shifted. * It's up to caller to free skb if everything was shifted. * - * If @tgt runs out of frags, the whole operation is aborted. + * If @tgt runs out of frags, or we cannot take an additional frag reference, + * the whole operation is aborted. * * Skb cannot include anything else but paged data while tgt is allowed * to have non-paged data as well. @@ -4409,7 +4410,9 @@ int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen) } else { skb_frag_page_copy(fragto, fragfrom); - skb_frag_ref(tgt, to); + /* Nothing has been committed yet. */ + if (!skb_frag_try_ref(tgt, to)) + return 0; skb_frag_off_copy(fragto, fragfrom); skb_frag_size_set(fragto, todo); @@ -5148,7 +5151,10 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, if (!skb_shinfo(nskb)->nr_frags) nskb->pp_recycle = frag_skb->pp_recycle; *nskb_frag = (i < 0) ? skb_head_frag_to_page_desc(frag_skb) : *frag; - skb_frag_ref(nskb, skb_shinfo(nskb)->nr_frags); + if (!skb_frag_try_ref(nskb, skb_shinfo(nskb)->nr_frags)) { + err = -ENOMEM; + goto err; + } size = skb_frag_size(nskb_frag); if (pos < offset) { @@ -6289,7 +6295,8 @@ bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from, bool *fragstolen, int *delta_truesize) { struct skb_shared_info *to_shinfo, *from_shinfo; - int i, delta, len = from->len; + int delta, len = from->len; + bool cloned; *fragstolen = false; @@ -6362,6 +6369,15 @@ bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from, WARN_ON_ONCE(delta < len); + /* + * A clone keeps referencing its frags, so we need our own references. + * Nothing was added to @to yet: we only steal the head of @from above + * if it is not locked, and the head of a clone is always locked. + */ + cloned = skb_cloned(from); + if (cloned && !skb_frags_try_ref(from)) + return false; + memcpy(to_shinfo->frags + to_shinfo->nr_frags, from_shinfo->frags, from_shinfo->nr_frags * sizeof(skb_frag_t)); @@ -6369,15 +6385,9 @@ bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from, if (from_shinfo->nr_frags) to_shinfo->flags |= from_shinfo->flags & SKBFL_SHARED_FRAG; - if (!skb_cloned(from)) + if (!cloned) from_shinfo->nr_frags = 0; - /* if the skb is not cloned this does nothing - * since we set nr_frags to 0. - */ - for (i = 0; i < from_shinfo->nr_frags; i++) - skb_frag_ref(from, i); - to->truesize += delta; to->len += len; to->data_len += len; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index cf3ddc055d48d..043838d996404 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2710,10 +2710,12 @@ static int tcp_clone_payload(struct sock *sk, struct sk_buff *to, } commit: WARN_ON_ONCE(len != probe_size); - for (i = 0; i < nr_frags; i++) - skb_frag_ref(to, i); - skb_shinfo(to)->nr_frags = nr_frags; + if (!skb_frags_try_ref(to)) { + skb_shinfo(to)->nr_frags = 0; + return -ENOMEM; + } + to->truesize += probe_size; to->len += probe_size; to->data_len += probe_size; -- 2.56.0