Skip to content

Commit 5891cd5

Browse files
edumazetdavem330
authored andcommitted
net_sched: add __rcu annotation to netdev->qdisc
syzbot found a data-race [1] which lead me to add __rcu annotations to netdev->qdisc, and proper accessors to get LOCKDEP support. [1] BUG: KCSAN: data-race in dev_activate / qdisc_lookup_rcu write to 0xffff888168ad6410 of 8 bytes by task 13559 on cpu 1: attach_default_qdiscs net/sched/sch_generic.c:1167 [inline] dev_activate+0x2ed/0x8f0 net/sched/sch_generic.c:1221 __dev_open+0x2e9/0x3a0 net/core/dev.c:1416 __dev_change_flags+0x167/0x3f0 net/core/dev.c:8139 rtnl_configure_link+0xc2/0x150 net/core/rtnetlink.c:3150 __rtnl_newlink net/core/rtnetlink.c:3489 [inline] rtnl_newlink+0xf4d/0x13e0 net/core/rtnetlink.c:3529 rtnetlink_rcv_msg+0x745/0x7e0 net/core/rtnetlink.c:5594 netlink_rcv_skb+0x14e/0x250 net/netlink/af_netlink.c:2494 rtnetlink_rcv+0x18/0x20 net/core/rtnetlink.c:5612 netlink_unicast_kernel net/netlink/af_netlink.c:1317 [inline] netlink_unicast+0x602/0x6d0 net/netlink/af_netlink.c:1343 netlink_sendmsg+0x728/0x850 net/netlink/af_netlink.c:1919 sock_sendmsg_nosec net/socket.c:705 [inline] sock_sendmsg net/socket.c:725 [inline] ____sys_sendmsg+0x39a/0x510 net/socket.c:2413 ___sys_sendmsg net/socket.c:2467 [inline] __sys_sendmsg+0x195/0x230 net/socket.c:2496 __do_sys_sendmsg net/socket.c:2505 [inline] __se_sys_sendmsg net/socket.c:2503 [inline] __x64_sys_sendmsg+0x42/0x50 net/socket.c:2503 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x44/0xd0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x44/0xae read to 0xffff888168ad6410 of 8 bytes by task 13560 on cpu 0: qdisc_lookup_rcu+0x30/0x2e0 net/sched/sch_api.c:323 __tcf_qdisc_find+0x74/0x3a0 net/sched/cls_api.c:1050 tc_del_tfilter+0x1c7/0x1350 net/sched/cls_api.c:2211 rtnetlink_rcv_msg+0x5ba/0x7e0 net/core/rtnetlink.c:5585 netlink_rcv_skb+0x14e/0x250 net/netlink/af_netlink.c:2494 rtnetlink_rcv+0x18/0x20 net/core/rtnetlink.c:5612 netlink_unicast_kernel net/netlink/af_netlink.c:1317 [inline] netlink_unicast+0x602/0x6d0 net/netlink/af_netlink.c:1343 netlink_sendmsg+0x728/0x850 net/netlink/af_netlink.c:1919 sock_sendmsg_nosec net/socket.c:705 [inline] sock_sendmsg net/socket.c:725 [inline] ____sys_sendmsg+0x39a/0x510 net/socket.c:2413 ___sys_sendmsg net/socket.c:2467 [inline] __sys_sendmsg+0x195/0x230 net/socket.c:2496 __do_sys_sendmsg net/socket.c:2505 [inline] __se_sys_sendmsg net/socket.c:2503 [inline] __x64_sys_sendmsg+0x42/0x50 net/socket.c:2503 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x44/0xd0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x44/0xae value changed: 0xffffffff85dee080 -> 0xffff88815d96ec00 Reported by Kernel Concurrency Sanitizer on: CPU: 0 PID: 13560 Comm: syz-executor.2 Not tainted 5.17.0-rc3-syzkaller-00116-gf1baf68e1383-dirty #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 Fixes: 470502d ("net: sched: unlock rules update API") Signed-off-by: Eric Dumazet <edumazet@google.com> Cc: Vlad Buslov <vladbu@mellanox.com> Reported-by: syzbot <syzkaller@googlegroups.com> Cc: Jamal Hadi Salim <jhs@mojatatu.com> Cc: Cong Wang <xiyou.wangcong@gmail.com> Cc: Jiri Pirko <jiri@resnulli.us> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent a261414 commit 5891cd5

File tree

5 files changed

+36
-29
lines changed

5 files changed

+36
-29
lines changed

include/linux/netdevice.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2158,7 +2158,7 @@ struct net_device {
21582158
struct netdev_queue *_tx ____cacheline_aligned_in_smp;
21592159
unsigned int num_tx_queues;
21602160
unsigned int real_num_tx_queues;
2161-
struct Qdisc *qdisc;
2161+
struct Qdisc __rcu *qdisc;
21622162
unsigned int tx_queue_len;
21632163
spinlock_t tx_global_lock;
21642164

net/core/rtnetlink.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1699,6 +1699,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,
16991699
{
17001700
struct ifinfomsg *ifm;
17011701
struct nlmsghdr *nlh;
1702+
struct Qdisc *qdisc;
17021703

17031704
ASSERT_RTNL();
17041705
nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ifm), flags);
@@ -1716,6 +1717,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,
17161717
if (tgt_netnsid >= 0 && nla_put_s32(skb, IFLA_TARGET_NETNSID, tgt_netnsid))
17171718
goto nla_put_failure;
17181719

1720+
qdisc = rtnl_dereference(dev->qdisc);
17191721
if (nla_put_string(skb, IFLA_IFNAME, dev->name) ||
17201722
nla_put_u32(skb, IFLA_TXQLEN, dev->tx_queue_len) ||
17211723
nla_put_u8(skb, IFLA_OPERSTATE,
@@ -1735,8 +1737,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,
17351737
#endif
17361738
put_master_ifindex(skb, dev) ||
17371739
nla_put_u8(skb, IFLA_CARRIER, netif_carrier_ok(dev)) ||
1738-
(dev->qdisc &&
1739-
nla_put_string(skb, IFLA_QDISC, dev->qdisc->ops->id)) ||
1740+
(qdisc &&
1741+
nla_put_string(skb, IFLA_QDISC, qdisc->ops->id)) ||
17401742
nla_put_ifalias(skb, dev) ||
17411743
nla_put_u32(skb, IFLA_CARRIER_CHANGES,
17421744
atomic_read(&dev->carrier_up_count) +

net/sched/cls_api.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,7 +1044,7 @@ static int __tcf_qdisc_find(struct net *net, struct Qdisc **q,
10441044

10451045
/* Find qdisc */
10461046
if (!*parent) {
1047-
*q = dev->qdisc;
1047+
*q = rcu_dereference(dev->qdisc);
10481048
*parent = (*q)->handle;
10491049
} else {
10501050
*q = qdisc_lookup_rcu(dev, TC_H_MAJ(*parent));
@@ -2587,7 +2587,7 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
25872587

25882588
parent = tcm->tcm_parent;
25892589
if (!parent)
2590-
q = dev->qdisc;
2590+
q = rtnl_dereference(dev->qdisc);
25912591
else
25922592
q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
25932593
if (!q)
@@ -2962,7 +2962,7 @@ static int tc_dump_chain(struct sk_buff *skb, struct netlink_callback *cb)
29622962
return skb->len;
29632963

29642964
if (!tcm->tcm_parent)
2965-
q = dev->qdisc;
2965+
q = rtnl_dereference(dev->qdisc);
29662966
else
29672967
q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
29682968

net/sched/sch_api.c

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle)
301301

302302
if (!handle)
303303
return NULL;
304-
q = qdisc_match_from_root(dev->qdisc, handle);
304+
q = qdisc_match_from_root(rtnl_dereference(dev->qdisc), handle);
305305
if (q)
306306
goto out;
307307

@@ -320,7 +320,7 @@ struct Qdisc *qdisc_lookup_rcu(struct net_device *dev, u32 handle)
320320

321321
if (!handle)
322322
return NULL;
323-
q = qdisc_match_from_root(dev->qdisc, handle);
323+
q = qdisc_match_from_root(rcu_dereference(dev->qdisc), handle);
324324
if (q)
325325
goto out;
326326

@@ -1082,10 +1082,10 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
10821082
skip:
10831083
if (!ingress) {
10841084
notify_and_destroy(net, skb, n, classid,
1085-
dev->qdisc, new);
1085+
rtnl_dereference(dev->qdisc), new);
10861086
if (new && !new->ops->attach)
10871087
qdisc_refcount_inc(new);
1088-
dev->qdisc = new ? : &noop_qdisc;
1088+
rcu_assign_pointer(dev->qdisc, new ? : &noop_qdisc);
10891089

10901090
if (new && new->ops->attach)
10911091
new->ops->attach(new);
@@ -1451,7 +1451,7 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
14511451
q = dev_ingress_queue(dev)->qdisc_sleeping;
14521452
}
14531453
} else {
1454-
q = dev->qdisc;
1454+
q = rtnl_dereference(dev->qdisc);
14551455
}
14561456
if (!q) {
14571457
NL_SET_ERR_MSG(extack, "Cannot find specified qdisc on specified device");
@@ -1540,7 +1540,7 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
15401540
q = dev_ingress_queue(dev)->qdisc_sleeping;
15411541
}
15421542
} else {
1543-
q = dev->qdisc;
1543+
q = rtnl_dereference(dev->qdisc);
15441544
}
15451545

15461546
/* It may be default qdisc, ignore it */
@@ -1762,7 +1762,8 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb)
17621762
s_q_idx = 0;
17631763
q_idx = 0;
17641764

1765-
if (tc_dump_qdisc_root(dev->qdisc, skb, cb, &q_idx, s_q_idx,
1765+
if (tc_dump_qdisc_root(rtnl_dereference(dev->qdisc),
1766+
skb, cb, &q_idx, s_q_idx,
17661767
true, tca[TCA_DUMP_INVISIBLE]) < 0)
17671768
goto done;
17681769

@@ -2033,7 +2034,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n,
20332034
} else if (qid1) {
20342035
qid = qid1;
20352036
} else if (qid == 0)
2036-
qid = dev->qdisc->handle;
2037+
qid = rtnl_dereference(dev->qdisc)->handle;
20372038

20382039
/* Now qid is genuine qdisc handle consistent
20392040
* both with parent and child.
@@ -2044,7 +2045,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n,
20442045
portid = TC_H_MAKE(qid, portid);
20452046
} else {
20462047
if (qid == 0)
2047-
qid = dev->qdisc->handle;
2048+
qid = rtnl_dereference(dev->qdisc)->handle;
20482049
}
20492050

20502051
/* OK. Locate qdisc */
@@ -2205,7 +2206,8 @@ static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb)
22052206
s_t = cb->args[0];
22062207
t = 0;
22072208

2208-
if (tc_dump_tclass_root(dev->qdisc, skb, tcm, cb, &t, s_t, true) < 0)
2209+
if (tc_dump_tclass_root(rtnl_dereference(dev->qdisc),
2210+
skb, tcm, cb, &t, s_t, true) < 0)
22092211
goto done;
22102212

22112213
dev_queue = dev_ingress_queue(dev);

net/sched/sch_generic.c

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,30 +1164,33 @@ static void attach_default_qdiscs(struct net_device *dev)
11641164
if (!netif_is_multiqueue(dev) ||
11651165
dev->priv_flags & IFF_NO_QUEUE) {
11661166
netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL);
1167-
dev->qdisc = txq->qdisc_sleeping;
1168-
qdisc_refcount_inc(dev->qdisc);
1167+
qdisc = txq->qdisc_sleeping;
1168+
rcu_assign_pointer(dev->qdisc, qdisc);
1169+
qdisc_refcount_inc(qdisc);
11691170
} else {
11701171
qdisc = qdisc_create_dflt(txq, &mq_qdisc_ops, TC_H_ROOT, NULL);
11711172
if (qdisc) {
1172-
dev->qdisc = qdisc;
1173+
rcu_assign_pointer(dev->qdisc, qdisc);
11731174
qdisc->ops->attach(qdisc);
11741175
}
11751176
}
1177+
qdisc = rtnl_dereference(dev->qdisc);
11761178

11771179
/* Detect default qdisc setup/init failed and fallback to "noqueue" */
1178-
if (dev->qdisc == &noop_qdisc) {
1180+
if (qdisc == &noop_qdisc) {
11791181
netdev_warn(dev, "default qdisc (%s) fail, fallback to %s\n",
11801182
default_qdisc_ops->id, noqueue_qdisc_ops.id);
11811183
dev->priv_flags |= IFF_NO_QUEUE;
11821184
netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL);
1183-
dev->qdisc = txq->qdisc_sleeping;
1184-
qdisc_refcount_inc(dev->qdisc);
1185+
qdisc = txq->qdisc_sleeping;
1186+
rcu_assign_pointer(dev->qdisc, qdisc);
1187+
qdisc_refcount_inc(qdisc);
11851188
dev->priv_flags ^= IFF_NO_QUEUE;
11861189
}
11871190

11881191
#ifdef CONFIG_NET_SCHED
1189-
if (dev->qdisc != &noop_qdisc)
1190-
qdisc_hash_add(dev->qdisc, false);
1192+
if (qdisc != &noop_qdisc)
1193+
qdisc_hash_add(qdisc, false);
11911194
#endif
11921195
}
11931196

@@ -1217,7 +1220,7 @@ void dev_activate(struct net_device *dev)
12171220
* and noqueue_qdisc for virtual interfaces
12181221
*/
12191222

1220-
if (dev->qdisc == &noop_qdisc)
1223+
if (rtnl_dereference(dev->qdisc) == &noop_qdisc)
12211224
attach_default_qdiscs(dev);
12221225

12231226
if (!netif_carrier_ok(dev))
@@ -1383,7 +1386,7 @@ static int qdisc_change_tx_queue_len(struct net_device *dev,
13831386
void dev_qdisc_change_real_num_tx(struct net_device *dev,
13841387
unsigned int new_real_tx)
13851388
{
1386-
struct Qdisc *qdisc = dev->qdisc;
1389+
struct Qdisc *qdisc = rtnl_dereference(dev->qdisc);
13871390

13881391
if (qdisc->ops->change_real_num_tx)
13891392
qdisc->ops->change_real_num_tx(qdisc, new_real_tx);
@@ -1447,7 +1450,7 @@ static void dev_init_scheduler_queue(struct net_device *dev,
14471450

14481451
void dev_init_scheduler(struct net_device *dev)
14491452
{
1450-
dev->qdisc = &noop_qdisc;
1453+
rcu_assign_pointer(dev->qdisc, &noop_qdisc);
14511454
netdev_for_each_tx_queue(dev, dev_init_scheduler_queue, &noop_qdisc);
14521455
if (dev_ingress_queue(dev))
14531456
dev_init_scheduler_queue(dev, dev_ingress_queue(dev), &noop_qdisc);
@@ -1475,8 +1478,8 @@ void dev_shutdown(struct net_device *dev)
14751478
netdev_for_each_tx_queue(dev, shutdown_scheduler_queue, &noop_qdisc);
14761479
if (dev_ingress_queue(dev))
14771480
shutdown_scheduler_queue(dev, dev_ingress_queue(dev), &noop_qdisc);
1478-
qdisc_put(dev->qdisc);
1479-
dev->qdisc = &noop_qdisc;
1481+
qdisc_put(rtnl_dereference(dev->qdisc));
1482+
rcu_assign_pointer(dev->qdisc, &noop_qdisc);
14801483

14811484
WARN_ON(timer_pending(&dev->watchdog_timer));
14821485
}

0 commit comments

Comments
 (0)