__tc_ctl_tclass() unconditionally calls tc_bind_tclass() after RTM_DELTCLASS even when tclass_del_notify() fails. On ingress/clsact qdiscs with shared blocks, the delete always fails with -EOPNOTSUPP, but the subsequent bind walk still runs. tc_bind_tclass() reaches tcf_node_bind() which calls tcf_block_q() on the shared block and dereferences the NULL qdisc pointer. Only perform the reverse bind walk when the class delete succeeds. Triggered by issuing RTM_DELTCLASS on an ingress/clsact pseudo-class that carries a shared block with a classifier using bind_class: $ tc qdisc add dev veth0 egress_block 22 clsact $ tc filter add block 22 pref 1 protocol ip handle 0x1 \ fw classid ffff:fff3 action drop $ tc class del dev veth0 classid ffff:fff3 RTNETLINK answers: Operation not supported Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000700 The NULL dereference occurs in tcf_node_bind() when sch_tree_lock() is called with the NULL return from tcf_block_q() on a shared block. Fixes: 07d79fc7d94e ("net_sched: add reverse binding for tc class") Signed-off-by: Qi Tang --- net/sched/sch_api.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index cc43e3f7574f..50d311ff1f2d 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -2251,7 +2251,8 @@ static int __tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, case RTM_DELTCLASS: err = tclass_del_notify(net, cops, skb, n, q, cl, extack); /* Unbind the class with flilters with 0 */ - tc_bind_tclass(q, portid, clid, 0); + if (!err) + tc_bind_tclass(q, portid, clid, 0); goto out; case RTM_GETTCLASS: err = tclass_get_notify(net, skb, n, q, cl, extack); -- 2.43.0