From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-ua1-f42.google.com (mail-ua1-f42.google.com [209.85.222.42]) (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 8A575440A3B for ; Thu, 23 Jul 2026 10:52:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.42 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784803977; cv=none; b=F9bSIkzp8HyJSUzPDlHxxaQAdDbHsYn9X4x3yNnW+msbto5su25KWv8zvCBaRzUr8Uhlcam555fykTV4v1F582XRQlzFtUo3H45Trd1LIZ4IpehKzOny43ekzU0JU0+ohR6vZun4wxXhmT64IzlAobR2kTp4uucG+vO+kitmEhU= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784803977; c=relaxed/simple; bh=2GjEekfhsKHZU2DhsV5Bo35uprF/sY8GoPIsoM20T48=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=X9Y7cjxlxLPcGJgh8QT97OOQAn2+e6Cr6uHKjctTRK4PwYkwCId864dn4KPd8meq8sIxNPiBswT12afzgu6HMD2rlc+P2NNp/gn/memsKMNQ83zJexTLFYr3+Xnj4fmwnWC+KzDCsFOOA4aAQtfiaQdQYIlaYGxJ0Eh23ANTadw= 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 (1024-bit key) header.d=mojatatu.com header.i=@mojatatu.com header.b=cFAn7Ath; arc=none smtp.client-ip=209.85.222.42 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 (1024-bit key) header.d=mojatatu.com header.i=@mojatatu.com header.b="cFAn7Ath" Received: by mail-ua1-f42.google.com with SMTP id a1e0cc1a2514c-966d70b9e1cso281254241.2 for ; Thu, 23 Jul 2026 03:52:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mojatatu.com; s=google; t=1784803970; x=1785408770; 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:content-type; bh=hNsDmqf0L2DQ923uqfgdUFWg8JI20GGO2Glmu9J3hdA=; b=cFAn7Ath6rFWrmGsm91NKjYqAGCL7LaIZSJokgoV6WGm4Kbxw5tMIi5pJy9n14b9Et ZzjlQG0Nsv6dA7IltByQWRfPCcJv5OSvYSwuy1YEhTwIPQgfp5aGGXyK9el/E6Qj5IlN 9POGxCvNvKWpMRMY8CILYiWtXUIhIq5J4qHXo= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784803970; x=1785408770; 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:content-type; bh=hNsDmqf0L2DQ923uqfgdUFWg8JI20GGO2Glmu9J3hdA=; b=OaLamtb9m9HlmvgWVP5kJOGf6l1aH72QiSO+SSDx7qc/thggHBQ8BML1jd4xHV1Meg PvKawDOWdnAFenh7EYjSb0qxyUXoNG5Twf/fmTxQIHq3l8pw/j2gXK8b/IDELFECUHly ASJ2QChsIvtke3Z2+wBuzIWY/yPfZTu64W5vdc/2D/xVgBJtjalKj8KDVEJlKjM/ARk1 TcwugLcY4ZXN629SWbH/yp4K2/jnOqW6OI2EpBPSTZAjW7EirHOEWa+lKnnlA0qmTV9n LiiEqBY+rLc917E8jApre8a7yInoMbnFQ8l4ghI3WSwbhWnw7ChUQBDYLAjwTm0wZqD1 D96Q== X-Gm-Message-State: AOJu0Yzl3L5CROkpGSBBJltCj2QVW6mIoc/6JWVydk+n44BET+o3w2jq iT8iXeq8V+tNFOikGY81fI4TV2PyECOwHLmKWkI/9cPJnNg3hiBU0DdI+DYb+J72pi3NhnsFe6Y YKiE= X-Gm-Gg: AR+sD11mT3SL3em+AmcA4hmz4HMrGPWTmmm/n8k7TvsFYF7zTb0kwgC8Ujl/4BdSPun dgsObwjk5pNr85W/MGiFifwdxgYaQw/n+wK4P4s65v4G3qkiZeuz5GmgYzF2flr57mJIcniOHAr avRC9hb5xBtw/tHjnheUMSC9Guh5FTfjYqMtU7iwNupMTRttNymCTLTsM7+YP+HCSwN1afOU5Xk 9BCptzToFBjcLshiM14scQO81I1kVeIx+F0ntajEBlxqswp48tthMyVrx0SpPqHZe2HQdqKChzI dqqZOIwrlKtdEz9lC2c67qLtTz9c3zfcEzRUpZASHYyQm7zatZO9zJh5easYjN9BtKKildegj7q d0q+q/lxuqbWJRVCM2+/s3EOcI2dHQLn0zhw0b3p82b1xWZDOQUFg1pZngnW/34gfiN6/a4XsH/ vlp6oanA== X-Received: by 2002:a05:6102:3f11:b0:738:472f:2cb3 with SMTP id ada2fe7eead31-74d5e3ce9a1mr1066069137.8.1784803970546; Thu, 23 Jul 2026 03:52:50 -0700 (PDT) Received: from majuu.waya ([184.144.29.222]) by smtp.gmail.com with ESMTPSA id ada2fe7eead31-74ad31c1fa4sm4675225137.1.2026.07.23.03.52.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Jul 2026 03:52:49 -0700 (PDT) From: Jamal Hadi Salim To: netdev@vger.kernel.org Cc: pabeni@redhat.com, kuba@kernel.org, davem@davemloft.net, edumazet@google.com, horms@kernel.org, john.fastabend@gmail.com, jiri@resnulli.us, zdi-disclosures@trendmicro.com, sh.kalluri129@gmail.com, victor@mojatatu.com, security@kernel.org, stable@vger.kernel.org, Jamal Hadi Salim , Santosh Kalluri Subject: [PATCH net 1/1] net/sched: cls_route: fix fastmap use-after-free on filter Date: Thu, 23 Jul 2026 06:52:10 -0400 Message-Id: <20260723105210.817079-1-jhs@mojatatu.com> X-Mailer: git-send-email 2.34.1 Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit A case of partially rcu'ed structs. The route4 classifier maintains a 16-slot fastmap cache that stores raw struct route4_filter pointers indexed by (id, iif). The reader (route4_classify) populates this cache via route4_set_fastmap() for every classified packet that hits a filter. The writer (route4_delete, route4_change) clears the cache via route4_reset_fastmap() before RCU-deferred kfree of the filter. This creates a UAF race: 1. Reader walks the RCU-protected bucket chain, finds filter f 2. Writer unlinks f, calls route4_reset_fastmap(), then tcf_queue_work() 3. Reader calls route4_set_fastmap() and writes f into the cache *after* the writer's reset, caching a pointer about to be freed 4. After the RCU grace period, kfree(f) executes 5. Next classified packet on the same (id, iif) tuple hits the stale fastmap entry and reads f->res from freed memory Reproduced with an mdelay(100) accelerator in route4_set_fastmap() and a concurrent add/delete stress test (provided by both zdi and Santosh) deletes the last filter on a tp. Both triggered KASAN slab-use-after-free reports in the route4 fastmap paths. Fix: Move the fastmap flush to the writer side under RTNL, ordered after synchronize_rcu(). This ensures stale fastmap entries written by in-flight readers are visible (readers have finished) and no new readers can find the unlinked filter, so the subsequent route4_reset_fastmap(head) flushes them safely while head is valid under RTNL. Note: This fix will slow down deletes and filter add/replace, since synchronize_rcu() is called under RTNL in route4_delete() and route4_change(). An alternative fix would have been to introduce refcnt to the head struct but that is a much bigger patch. We justify this as a reasonable fix. Fixes: 1109c00547fc ("net: sched: RCU cls_route") Reported-by: zdi-disclosures@trendmicro.com Reported-by: Santosh Kalluri Suggested-by: Santosh Kalluri Tested-by: Victor Nogueira Tested-by: Santosh Kalluri Signed-off-by: Jamal Hadi Salim --- net/sched/cls_route.c | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index bd6f945bd388..700f878fc3cd 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -297,16 +297,29 @@ static void route4_destroy(struct tcf_proto *tp, bool rtnl_held, next = rtnl_dereference(f->next); RCU_INIT_POINTER(b->ht[h2], next); tcf_unbind_filter(tp, &f->res); - if (tcf_exts_get_net(&f->exts)) - route4_queue_work(f); - else - __route4_delete_filter(f); + /* Always defer the free. The direct + * __route4_delete_filter() path has no + * grace period and races with readers + * that cached f in the fastmap. + */ + tcf_exts_get_net(&f->exts); + route4_queue_work(f); } } RCU_INIT_POINTER(head->table[h1], NULL); kfree_rcu(b, rcu); } } + + /* All filters are unlinked; no new reader can find them on the + * chain. Wait for in-flight readers that may still hold a filter + * pointer and have published it into the fastmap after we unlinked. + * Then flush the stale entries while head is still valid under + * RTNL, matching the route4_delete() pattern. + */ + synchronize_rcu(); + route4_reset_fastmap(head); + kfree_rcu(head, rcu); } @@ -334,10 +347,16 @@ static int route4_delete(struct tcf_proto *tp, void *arg, bool *last, /* unlink it */ RCU_INIT_POINTER(*fp, rtnl_dereference(f->next)); - /* Remove any fastmap lookups that might ref filter - * notice we unlink'd the filter so we can't get it - * back in the fastmap. + /* This code path assumes we have the RTNL lock. + * We wait for in-flight readers that may still hold + * the filter pointer and publish it into the fastmap + * after we unlinked it. + * Once done, no new reader can find the filter on the + * chain, so the only stale fastmap entries are the + * ones those readers just wrote. Flush them now + * while head is still valid. */ + synchronize_rcu(); route4_reset_fastmap(head); /* Delete it */ @@ -558,6 +577,15 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, } } + /* Assuming RTNL lock. Wait for in-flight readers that may still + * hold a filter pointer and publish it into the fastmap after we + * inserted the new filter (or unlinked fold when replacing). + * Once they are done, flush the stale entries while head is still + * valid. Needed on both the creation and replace paths: on + * creation, readers may have cached a ROUTE4_FAILURE entry for the + * (id, iif) tuple that the new filter now matches. + */ + synchronize_rcu(); route4_reset_fastmap(head); *arg = f; if (fold) { -- 2.34.1