summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2026-04-28 04:41:37 +0300
committerJakub Kicinski <kuba@kernel.org>2026-04-28 04:41:38 +0300
commitc4047e7075b303b952a595f901b0f022c2403206 (patch)
treedabf81e73be562d555e9d59e2fddf1adb4caffb8
parenta9bc28aa4e64320668131349436a650bf42591a5 (diff)
parenta469feed399da791f890b3448622121e97a07f3b (diff)
downloadlinux-c4047e7075b303b952a595f901b0f022c2403206.tar.xz
Merge branch 'net-sched-taprio-fix-null-pointer-dereference-in-class-dump'
Weiming Shi says: ==================== net/sched: taprio: fix NULL pointer dereference in class dump Patch 1/2 is the fix: replace NULL entries in q->qdiscs[] with the global &noop_qdisc singleton so that control-plane dump paths, as well as the existing NULL guards in the data-plane enqueue/dequeue paths, cannot deref a NULL child qdisc. Patch 2/2 is a tdc regression test that drives the graft + delete + class-dump sequence on a multi-queue netdevsim device. It panics the vulnerable kernel and passes on the fixed one. ==================== Link: https://patch.msgid.link/20260422161958.2517539-2-bestswngs@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-rw-r--r--net/sched/sch_taprio.c13
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json26
2 files changed, 34 insertions, 5 deletions
diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c
index a47a09d76400..45245157e00a 100644
--- a/net/sched/sch_taprio.c
+++ b/net/sched/sch_taprio.c
@@ -634,7 +634,7 @@ static int taprio_enqueue(struct sk_buff *skb, struct Qdisc *sch,
queue = skb_get_queue_mapping(skb);
child = q->qdiscs[queue];
- if (unlikely(!child))
+ if (unlikely(child == &noop_qdisc))
return qdisc_drop(skb, sch, to_free);
if (taprio_skb_exceeds_queue_max_sdu(sch, skb)) {
@@ -717,7 +717,7 @@ static struct sk_buff *taprio_dequeue_from_txq(struct Qdisc *sch, int txq,
int len;
u8 tc;
- if (unlikely(!child))
+ if (unlikely(child == &noop_qdisc))
return NULL;
if (TXTIME_ASSIST_IS_ENABLED(q->flags))
@@ -2184,6 +2184,9 @@ static int taprio_graft(struct Qdisc *sch, unsigned long cl,
if (!dev_queue)
return -EINVAL;
+ if (!new)
+ new = &noop_qdisc;
+
if (dev->flags & IFF_UP)
dev_deactivate(dev, false);
@@ -2197,14 +2200,14 @@ static int taprio_graft(struct Qdisc *sch, unsigned long cl,
*old = q->qdiscs[cl - 1];
if (FULL_OFFLOAD_IS_ENABLED(q->flags)) {
WARN_ON_ONCE(dev_graft_qdisc(dev_queue, new) != *old);
- if (new)
+ if (new != &noop_qdisc)
qdisc_refcount_inc(new);
- if (*old)
+ if (*old && *old != &noop_qdisc)
qdisc_put(*old);
}
q->qdiscs[cl - 1] = new;
- if (new)
+ if (new != &noop_qdisc)
new->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT;
if (dev->flags & IFF_UP)
diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json
index 557fb074acf0..cd19d05925e4 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json
@@ -302,5 +302,31 @@
"$TC qdisc del dev $ETH root",
"echo \"1\" > /sys/bus/netdevsim/del_device"
]
+ },
+ {
+ "id": "c7e1",
+ "name": "Class dump after graft and delete of explicit child qdisc",
+ "category": [
+ "qdisc",
+ "taprio"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ "echo \"1 1 8\" > /sys/bus/netdevsim/new_device",
+ "$TC qdisc replace dev $ETH handle 8001: parent root taprio num_tc 8 map 0 1 2 3 4 5 6 7 queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 base-time 0 sched-entry S ff 20000000 clockid CLOCK_TAI",
+ "$TC qdisc add dev $ETH parent 8001:1 handle 8002: pfifo",
+ "$TC qdisc del dev $ETH parent 8001:1 handle 8002:"
+ ],
+ "cmdUnderTest": "$TC class show dev $ETH",
+ "expExitCode": "0",
+ "verifyCmd": "$TC class show dev $ETH",
+ "matchPattern": "class taprio 8001:[0-9]+ root",
+ "matchCount": "8",
+ "teardown": [
+ "$TC qdisc del dev $ETH root",
+ "echo \"1\" > /sys/bus/netdevsim/del_device"
+ ]
}
]