From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-qt1-f172.google.com (mail-qt1-f172.google.com [209.85.160.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1CFF83998AB for ; Tue, 26 May 2026 15:59:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.172 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779811163; cv=none; b=jj3vamWETAvh10H1YcGHW5vtINcV18I759DHDrNJoB/MJcf74SdLVaHovSng4vGV8p7XZ1sIE2q/wBHImMysyEIPxJy5upkR4aCg/bWZS54L2LZjkAcBRZCNFoy93iM4RjfdP6bZlvKBBTWNPaWB55HBobFBIN0SJL0tgwIvRDM= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779811163; c=relaxed/simple; bh=Dluq2Rs+5vVeLTQfXUj3g/gwAUzCpuoB4+dwHvPmJTs=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=Z71ITbEiakXufgIUzFoWcrETQPZYMG0gZD/U0KZv3ro39Mg1u9irVEkcYILDEfjf4h29h7WtPw07dwLOrBKZjsADH2qMrxeCdZNnUVFfK3GW3klNVwdIx+SpyX8lpWBoPTCuJ/hZvph/j3J2sJFSP2fel2abfhf9OpYO1h6b11E= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=mojatatu.com; spf=none smtp.mailfrom=mojatatu.com; dkim=pass (2048-bit key) header.d=mojatatu-com.20251104.gappssmtp.com header.i=@mojatatu-com.20251104.gappssmtp.com header.b=xE8UlafW; arc=none smtp.client-ip=209.85.160.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=mojatatu.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=mojatatu.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=mojatatu-com.20251104.gappssmtp.com header.i=@mojatatu-com.20251104.gappssmtp.com header.b="xE8UlafW" Received: by mail-qt1-f172.google.com with SMTP id d75a77b69052e-516d634956fso50796881cf.2 for ; Tue, 26 May 2026 08:59:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mojatatu-com.20251104.gappssmtp.com; s=20251104; t=1779811159; x=1780415959; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=32IHWahZEAP3hoiT+rYy+Q30taja0WRr+TrxoxUOOZc=; b=xE8UlafWbe3gIUwaCIhzy+NcvuLwF1mqQgLSzKELON4jMUHCNvk1KXQgVyyA1241i+ CtJZpGklFd+nLtsV2WX7cQqHE03Ji58rAU27mO8w4EExjUlnaIf+PtN0z54ON025zLE+ sIDHpapv9VsE2wlp4jmDhEh7NmnZskkOyV89w0W+SXk8w3KKPCWbX+xqCRDOF62+3Q5T 9O0RBo7uQ0npoZlSqOaH5q2/5IxlW9N2t9w/zUK57DSGcqZpOYVRuyuYLtdQWom53lmE BcFAH3OCeAj9ybYkxdlceWRe8XVRwMOOwysnQhh5fbcKdpqBBL/GdlDVqBbwMzS8av3P VR3w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779811159; x=1780415959; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=32IHWahZEAP3hoiT+rYy+Q30taja0WRr+TrxoxUOOZc=; b=ALLV+LgocBDsZkVX2eK7JPwcs5LMMQ35shEcZ1WVUI5XoQAA/6jmSl0nxhr2EF01/a Tbdw19vnhuCRJUoyjYTuwA0oHP5kY2YhoRWqlRUGRH2DGevA2AxTKevKSayFymWmZNTc 59Y4pdPA1AnIVSEuwDeui9Masyn+W9x3pgk3+pd8gFBs3OcgowzoMAfQfrShNp6dDBaw tyLNZ5fw68zBBM6zkrwNd8irAu+GZ8AFKz69a+zlIVVI40qPMZVMLk/h8BJWjdgZ4KJT kB0CGb1LOLk0qgjijILTyRfBupKgwFts35eKZjTcIjS2LOB5V0bo4fIsGVNNKZHpp5lN Mnkw== X-Forwarded-Encrypted: i=1; AFNElJ/GMvhLQhn4VyrZFSgcFxoP2CssHro2ju1MAimejRw2dEFmBP/9iQP+JDjle6joOLzScLXiBBeKxL3FUYg=@vger.kernel.org X-Gm-Message-State: AOJu0YzN9e6tAZmYnc1WtcbT6+8NV9PDhqPg3ssVWu2pdNfQiD7pEXXq HMNwYZt3OXh/CnvilQIAYDsXh8rQtkIqLpgYrCnzw8P9bvvMAXPBSDlVuocFJwfzog== X-Gm-Gg: Acq92OG5MzqO7/oyMbqE0AXgxsSo0p/87X6Wy1I4tF8yH50HvEb7xxamrfMiuMBDQcW AOGaQ8YSEZ8sq5MCHVtMqAkCRXppHvdvQHCf1q2CdL4y426Un3Pj9Al+XC24mT+9LoaG1ZbyZlE bze1sBj3PeoZ5mabkpqCSz+A+NyJ/96iKuYNwre4MZejaFSYd4mDuEXCA6vABUllXYpe3Ukl1SS XTQtjdDrqiaRxOY7MmBTKRzr8Fsw7WWotNU58K1lK9OPlRAerEyRDD4WKaIEkMqYP+cS8EZClE/ H61Z+M0TmZ4nRD3eCDpv2czBGZe9ACHA2S5GbaIYY8aDLkkIVVND3I3nTATWjv8wFKcBeIUVYg6 HUizLmemNyXbOS1ItME9IPzDf+yeT/fP0EznXPAkx6cf+YIxb12WIzFPQROuZHpqq4qxTlSMjx0 4mukmFgRCEH24P7NY2TOgtSwdhZurFPr2XQNWb7VBf3Yee4cWMJfIVxv4YPmAPJ0TEHtt8pwEwW uexWAotY/qLyRIrMAFW X-Received: by 2002:a05:622a:11:b0:516:e569:d3f5 with SMTP id d75a77b69052e-516e569d943mr172627881cf.15.1779811158459; Tue, 26 May 2026 08:59:18 -0700 (PDT) Received: from majuu.waya (bras-base-kntaon1621w-grc-04-184-144-29-222.dsl.bell.ca. [184.144.29.222]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-51706b081c7sm21645681cf.28.2026.05.26.08.59.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 26 May 2026 08:59:17 -0700 (PDT) From: Jamal Hadi Salim To: netdev@vger.kernel.org Cc: davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, horms@kernel.org, jiri@resnulli.us, victor@mojatatu.com, david.laight.linux@gmail.com, yimingqian591@gmail.com, keenanat2000@gmail.com, 2045gemini@gmail.com, rollkingzzc@gmail.com, toke@redhat.com, dcaratti@redhat.com, security@kernel.org, linux-kernel@vger.kernel.org, Rajat Gupta , Jamal Hadi Salim Subject: [PATCH net v2 1/1] net/sched: fix pedit partial COW leading to page cache corruption Date: Tue, 26 May 2026 11:59:13 -0400 Message-Id: <20260526155913.1060694-1-jhs@mojatatu.com> X-Mailer: git-send-email 2.34.1 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Rajat Gupta tcf_pedit_act() computes the COW range for skb_ensure_writable() once before the key loop using tcfp_off_max_hint, but the hint does not account for the runtime header offset added by typed keys. This can leave part of the write region un-COW'd. Fix by moving skb_ensure_writable() inside the per-key loop where the actual write offset is known, and add overflow checking on the offset arithmetic. For negative offsets (e.g. Ethernet header edits at ingress), use skb_cow() to COW the headroom instead. Guard offset_valid() against INT_MIN, where negation is undefined. Additionally, linearize skbs with shared frags upfront to prevent silent data corruption when pedit operates on zero-copy pages (e.g. from sendfile). Fixes: 8b796475fd78 ("net/sched: act_pedit: really ensure the skb is writable") Reported-by: Yiming Qian Reported-by: Keenan Dong Reported-by: Han Guidong <2045gemini@gmail.com> Reported-by: Zhang Cen Tested-by: Victor Nogueira Acked-by: Jamal Hadi Salim Signed-off-by: Rajat Gupta --- Changes v1->v2: 1. Do better boundary analysis to cover cloned skbs with frags. Pointed out by sashiko-nipa: https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260519033950.2037-1-rajat.gupta%40oss.qualcomm.com 2. As a result of fix #1 remove the skb_has_shared_frag() check, unnecessary. Also Jakub has plans where the shared frags is not going to be a "thing" 3. Make small adjustments everywhere for integer checks, suggested by D. Laight 4. Remove all reviewers and testers since this is a large enough change. Please retest and re-review. 5. Remove Rajat as reporter since he is the author (which implies he is a reporter) --- net/sched/act_pedit.c | 49 ++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index bc20f08a2789..719bee335e1f 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -323,7 +324,7 @@ static bool offset_valid(struct sk_buff *skb, int offset) if (offset > 0 && offset > skb->len) return false; - if (offset < 0 && -offset > skb_headroom(skb)) + if (offset < 0 && offset < -(int)skb_headroom(skb)) return false; return true; @@ -393,18 +394,10 @@ TC_INDIRECT_SCOPE int tcf_pedit_act(struct sk_buff *skb, struct tcf_pedit_key_ex *tkey_ex; struct tcf_pedit_parms *parms; struct tc_pedit_key *tkey; - u32 max_offset; int i; parms = rcu_dereference_bh(p->parms); - max_offset = (skb_transport_header_was_set(skb) ? - skb_transport_offset(skb) : - skb_network_offset(skb)) + - parms->tcfp_off_max_hint; - if (skb_ensure_writable(skb, min(skb->len, max_offset))) - goto done; - tcf_lastuse_update(&p->tcf_tm); tcf_action_update_bstats(&p->common, skb); @@ -412,9 +405,10 @@ TC_INDIRECT_SCOPE int tcf_pedit_act(struct sk_buff *skb, tkey_ex = parms->tcfp_keys_ex; for (i = parms->tcfp_nkeys; i > 0; i--, tkey++) { + int write_offset, write_len; int offset = tkey->off; int hoffset = 0; - u32 *ptr, hdata; + u32 *ptr; u32 val; int rc; @@ -451,15 +445,38 @@ TC_INDIRECT_SCOPE int tcf_pedit_act(struct sk_buff *skb, } } - if (!offset_valid(skb, hoffset + offset)) { - pr_info_ratelimited("tc action pedit offset %d out of bounds\n", hoffset + offset); + if (unlikely(check_add_overflow(hoffset, offset, + &write_offset))) { + pr_info_ratelimited("tc action pedit offset overflow\n"); goto bad; } - ptr = skb_header_pointer(skb, hoffset + offset, - sizeof(hdata), &hdata); - if (!ptr) + if (!offset_valid(skb, write_offset)) { + pr_info_ratelimited("tc action pedit offset %d out of bounds\n", + write_offset); goto bad; + } + + if (write_offset < 0) { + if (skb_cow(skb, -write_offset)) + goto bad; + if (write_offset + (int)sizeof(*ptr) > 0) { + if (skb_ensure_writable(skb, + min(skb->len, + write_offset + sizeof(*ptr)))) + goto bad; + } + } else { + if (unlikely(check_add_overflow(write_offset, + (int)sizeof(*ptr), + &write_len))) + goto bad; + if (skb_ensure_writable(skb, min_t(int, skb->len, + write_len))) + goto bad; + } + + ptr = (u32 *)(skb->data + write_offset); /* just do it, baby */ switch (cmd) { case TCA_PEDIT_KEY_EX_CMD_SET: @@ -474,8 +491,6 @@ TC_INDIRECT_SCOPE int tcf_pedit_act(struct sk_buff *skb, } *ptr = ((*ptr & tkey->mask) ^ val); - if (ptr == &hdata) - skb_store_bits(skb, hoffset + offset, ptr, 4); } goto done; -- 2.34.1