Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: bridge: annotate data-races around fdb->{updated,used}

fdb->updated and fdb->used are read and written locklessly.

Add READ_ONCE()/WRITE_ONCE() annotations.

Fixes: 31cbc39b6344 ("net: bridge: add option to allow activity notifications for any fdb entries")
Reported-by: syzbot+bfab43087ad57222ce96@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/netdev/695e3d74.050a0220.1c677c.035f.GAE@google.com/
Signed-off-by: Eric Dumazet <edumazet@google.com>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
Link: https://patch.msgid.link/20260108093806.834459-1-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Eric Dumazet and committed by
Jakub Kicinski
b25a0b4a 872ac785

+18 -14
+16 -12
net/bridge/br_fdb.c
··· 70 70 { 71 71 return !test_bit(BR_FDB_STATIC, &fdb->flags) && 72 72 !test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags) && 73 - time_before_eq(fdb->updated + hold_time(br), jiffies); 73 + time_before_eq(READ_ONCE(fdb->updated) + hold_time(br), jiffies); 74 74 } 75 75 76 76 static int fdb_to_nud(const struct net_bridge *br, ··· 126 126 if (nla_put_u32(skb, NDA_FLAGS_EXT, ext_flags)) 127 127 goto nla_put_failure; 128 128 129 - ci.ndm_used = jiffies_to_clock_t(now - fdb->used); 129 + ci.ndm_used = jiffies_to_clock_t(now - READ_ONCE(fdb->used)); 130 130 ci.ndm_confirmed = 0; 131 - ci.ndm_updated = jiffies_to_clock_t(now - fdb->updated); 131 + ci.ndm_updated = jiffies_to_clock_t(now - READ_ONCE(fdb->updated)); 132 132 ci.ndm_refcnt = 0; 133 133 if (nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci)) 134 134 goto nla_put_failure; ··· 551 551 */ 552 552 rcu_read_lock(); 553 553 hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) { 554 - unsigned long this_timer = f->updated + delay; 554 + unsigned long this_timer = READ_ONCE(f->updated) + delay; 555 555 556 556 if (test_bit(BR_FDB_STATIC, &f->flags) || 557 557 test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &f->flags)) { ··· 924 924 { 925 925 struct net_bridge_fdb_entry *f; 926 926 struct __fdb_entry *fe = buf; 927 + unsigned long delta; 927 928 int num = 0; 928 929 929 930 memset(buf, 0, maxnum*sizeof(struct __fdb_entry)); ··· 954 953 fe->port_hi = f->dst->port_no >> 8; 955 954 956 955 fe->is_local = test_bit(BR_FDB_LOCAL, &f->flags); 957 - if (!test_bit(BR_FDB_STATIC, &f->flags)) 958 - fe->ageing_timer_value = jiffies_delta_to_clock_t(jiffies - f->updated); 956 + if (!test_bit(BR_FDB_STATIC, &f->flags)) { 957 + delta = jiffies - READ_ONCE(f->updated); 958 + fe->ageing_timer_value = 959 + jiffies_delta_to_clock_t(delta); 960 + } 959 961 ++fe; 960 962 ++num; 961 963 } ··· 1006 1002 unsigned long now = jiffies; 1007 1003 bool fdb_modified = false; 1008 1004 1009 - if (now != fdb->updated) { 1010 - fdb->updated = now; 1005 + if (now != READ_ONCE(fdb->updated)) { 1006 + WRITE_ONCE(fdb->updated, now); 1011 1007 fdb_modified = __fdb_mark_active(fdb); 1012 1008 } 1013 1009 ··· 1246 1242 if (fdb_handle_notify(fdb, notify)) 1247 1243 modified = true; 1248 1244 1249 - fdb->used = jiffies; 1245 + WRITE_ONCE(fdb->used, jiffies); 1250 1246 if (modified) { 1251 1247 if (refresh) 1252 - fdb->updated = jiffies; 1248 + WRITE_ONCE(fdb->updated, jiffies); 1253 1249 fdb_notify(br, fdb, RTM_NEWNEIGH, true); 1254 1250 } 1255 1251 ··· 1560 1556 goto err_unlock; 1561 1557 } 1562 1558 1563 - fdb->updated = jiffies; 1559 + WRITE_ONCE(fdb->updated, jiffies); 1564 1560 1565 1561 if (READ_ONCE(fdb->dst) != p) { 1566 1562 WRITE_ONCE(fdb->dst, p); ··· 1569 1565 1570 1566 if (test_and_set_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags)) { 1571 1567 /* Refresh entry */ 1572 - fdb->used = jiffies; 1568 + WRITE_ONCE(fdb->used, jiffies); 1573 1569 } else { 1574 1570 modified = true; 1575 1571 }
+2 -2
net/bridge/br_input.c
··· 221 221 if (test_bit(BR_FDB_LOCAL, &dst->flags)) 222 222 return br_pass_frame_up(skb, false); 223 223 224 - if (now != dst->used) 225 - dst->used = now; 224 + if (now != READ_ONCE(dst->used)) 225 + WRITE_ONCE(dst->used, now); 226 226 br_forward(dst->dst, skb, local_rcv, false); 227 227 } else { 228 228 if (!mcast_hit)