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=1794524141; b=5l4NagXH3hpetvNpc66eZU3yBoZtlgew0g0Lyk5Rq2nSn5SiwM8XdLcqSo8y/8JoYEu8 zj3WrOKiqTJusE5v4rOqdElAXDYndEalqbCK3A51MuOC2bgufQNTMZeKJ5D9Pf9aAG0z7 zqewKCwPNngE1k7tjtflgKhnPYoMOPOCShSWHATAD7UVk018jt1v9opUpZFQbKBtBT5cC SJgECcdgqPdVVKT+g8yBbhDjJeJ/isfEtRfs+CKmLIx36I7TSAu3AYx9igmBNBwZc7tq5 qxlxWg+JftxWQW62zVB8gptgauqM0YpN83gxGGw+lkan9CcFGF55R/yOxzoU9dnvK5Q== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1794524141; 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=UdFVWlgHbk5l/x1Pw82lSEvoK/V78BgFQoecwKNgz0w=; b=HKaOHcxqz3eExwHsyIR/WJw2Hpq2PyDnorYQkLLTOyKF8cK+QOual7NQArRLMQqkzr3S fJfRVWRe4AoOghjkRT857qrYe6HWN/G4SfK3KL33g3yMCn8tiAGt6bjx0N8eH0ZwkJnm/ HWfVxf83YdRp+qELBbu4NI5BcYk5wiZQbqRCIHr68U3j/1NXuZ0eV8FE91L+oDuHhSEIf CZVnVVX7ibHFtHE+u3ybGCIfpgRxUo7flT7rBADf5TY2qYyiscEiRuKWApFaBMdGlICZp L8PBdcxnqL8YfXjcWjQLbdAlXe79TT80ko6749CYRNHqXXlirKWPVTV00mcpnDpzyJg== 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="Z/uFEpdE"; 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 E03A8606A333; Thu, 12 Nov 2026 22:55:40 +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=Z/uFEpdE Received: by smtp.kernel.org (Postfix) with ESMTPSA id AC82A1F000C4; Thu, 12 Nov 2026 22:55:24 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1794524140; bh=UdFVWlgHbk5l/x1Pw82lSEvoK/V78BgFQoecwKNgz0w=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=Z/uFEpdEO0rIVY3WTFMr/D+S9rwYzMUI1B5YuFxAAs38CsATT3uSy0oyQMUEbc/GJ xmmj6U25hlmcvA/jT0m/BE4ZiZk1xAnD2dKnTxeSADrLschmBQCHvnow7VneK7EAbo jwBojbsiTvgnhiFrEikRzZfof4gpyoMbGJAd5iRTfzVLRISzJ7coGIrYmzAp9ojFmg KBzkUzEGZX6SdGV44zqVDAz/SHXsG81Ag66CiB95bYhQZUeHcwogT4r4YLwdiDMzAH mTr7FAksqNjM5atHqfA2nCIzLpGPqVxS2zBenxCGIqQ4fiLzuJXxSXv03ucmlUU0eK DVUvJuekiS7FQ== From: "David Hildenbrand (Arm)" Date: Thu, 12 Nov 2026 23:53:26 +0100 Subject: [PATCH v4 7/9] net: limit folio refcount when rearranging skb frags 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-7-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=5075; i=david@kernel.org; h=from:subject:message-id; bh=iaB52FMlmu0+VYWn1iELLkUK/yhgYJQLwhZnKBCgXsU=; b=owGbwMvMwCF2/OVpCdfQ3ZWMp9WSGLK+OWdlq0wQUJk/h3On/UXvC58+Vh7ds+DYReu2u7bn2 RtaVqzI6yhlYRDjYJAVU2QJCr5v8PLlutpqNz5mmDmsTCBDGLg4BWAily8yMixob90cMP+vEMOt 92fMnfpsjhWfffU39zqribyE6df/J8IZGWaylqoGVjpY7So6uf2e23pVgf1B676dm7m5ZfvhfQ9 4algB 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 Acked-by: Pedro Falcato 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