[PATCH net] net/sched: act_csum: Fix missing headroom COW and integer underflow in header rewriting

Muhammad Bilal <[email protected]>
Newsgroups org.kernel.vger.netdev,org.kernel.vger.linux-kernel,org.kernel.vger.stable
Message-ID <[email protected]>
tcf_csum_skb_nextlayer() and tcf_csum_ipv4() access and rewrite IP and L4
headers based on ntkoff = skb_network_offset(skb).

When the network header resides in the headroom (ntkoff < 0):
1. hl + ntkoff or sizeof(*iph) + ntkoff can evaluate to a negative value
   or underflow when passed to functions expecting unsigned lengths, such
   as pskb_may_pull() and skb_try_make_writable().
2. skb_try_make_writable() only evaluates writability from skb->data
   forwards and does not invoke skb_cow() on the headroom. When modifying
   cloned SKBs (e.g. from packet sockets, tc mirred, or BPF redirects),
   updating headers via ip_send_check() or L4 checksum replacements mutates
   shared headroom data directly, leading to packet corruption and page
   cache corruption.

Fix this by introducing a helper csum_ensure_writable() that validates
headroom using skb_cow(skb, -offset) when offset is negative before
ensuring writability across the modified header length.

Fixes: eb4d40654505 ("net/sched: add ACT_CSUM action to update packets checksums")
Signed-off-by: Muhammad Bilal <[email protected]>
---
 net/sched/act_csum.c | 21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)

diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c
index a8e2b83ebae5..cd2a6e974e6f 100644
--- a/net/sched/act_csum.c
+++ b/net/sched/act_csum.c
@@ -124,6 +124,18 @@ static int tcf_csum_init(struct net *net, struct nlattr *nla,
 	return err;
 }
 
+static int csum_ensure_writable(struct sk_buff *skb, int offset, size_t len)
+{
+	if (offset < 0) {
+		if (skb_cow(skb, -offset))
+			return -ENOMEM;
+		if (offset + (int)len > 0)
+			return skb_ensure_writable(skb, offset + len);
+		return 0;
+	}
+	return skb_ensure_writable(skb, offset + len);
+}
+
 /**
  * tcf_csum_skb_nextlayer - Get next layer pointer
  * @skb: sk_buff to use
@@ -139,8 +151,7 @@ static void *tcf_csum_skb_nextlayer(struct sk_buff *skb,
 	int ntkoff = skb_network_offset(skb);
 	int hl = ihl + jhl;
 
-	if (!pskb_may_pull(skb, ipl + ntkoff) || (ipl < hl) ||
-	    skb_try_make_writable(skb, hl + ntkoff))
+	if (ipl < hl || csum_ensure_writable(skb, ntkoff, max_t(unsigned int, ipl, hl)))
 		return NULL;
 	else
 		return (void *)(skb_network_header(skb) + ihl);
@@ -437,8 +448,8 @@ static int tcf_csum_ipv4(struct sk_buff *skb, u32 update_flags)
 	}
 
 	if (update_flags & TCA_CSUM_UPDATE_FLAG_IPV4HDR) {
-		if (skb_try_make_writable(skb, sizeof(*iph) + ntkoff))
+		if (csum_ensure_writable(skb, ntkoff, sizeof(*iph)))
 			goto fail;
 
 		ip_send_check(ip_hdr(skb));
 	}
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.