Skip to content

Commit 28006e6

Browse files
committed
net, neigh: Do not trigger immediate probes on NUD_FAILED from neigh_managed_work
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2193175 commit 4a81f6d Author: Daniel Borkmann <daniel@iogearbox.net> Date: Tue Feb 1 20:39:42 2022 +0100 net, neigh: Do not trigger immediate probes on NUD_FAILED from neigh_managed_work syzkaller was able to trigger a deadlock for NTF_MANAGED entries [0]: kworker/0:16/14617 is trying to acquire lock: ffffffff8d4dd370 (&tbl->lock){++-.}-{2:2}, at: ___neigh_create+0x9e1/0x2990 net/core/neighbour.c:652 [...] but task is already holding lock: ffffffff8d4dd370 (&tbl->lock){++-.}-{2:2}, at: neigh_managed_work+0x35/0x250 net/core/neighbour.c:1572 The neighbor entry turned to NUD_FAILED state, where __neigh_event_send() triggered an immediate probe as per commit cd28ca0 ("neigh: reduce arp latency") via neigh_probe() given table lock was held. One option to fix this situation is to defer the neigh_probe() back to the neigh_timer_handler() similarly as pre cd28ca0. For the case of NTF_MANAGED, this deferral is acceptable given this only happens on actual failure state and regular / expected state is NUD_VALID with the entry already present. The fix adds a parameter to __neigh_event_send() in order to communicate whether immediate probe is allowed or disallowed. Existing call-sites of neigh_event_send() default as-is to immediate probe. However, the neigh_managed_work() disables it via use of neigh_event_send_probe(). [0] <TASK> __dump_stack lib/dump_stack.c:88 [inline] dump_stack_lvl+0xcd/0x134 lib/dump_stack.c:106 print_deadlock_bug kernel/locking/lockdep.c:2956 [inline] check_deadlock kernel/locking/lockdep.c:2999 [inline] validate_chain kernel/locking/lockdep.c:3788 [inline] __lock_acquire.cold+0x149/0x3ab kernel/locking/lockdep.c:5027 lock_acquire kernel/locking/lockdep.c:5639 [inline] lock_acquire+0x1ab/0x510 kernel/locking/lockdep.c:5604 __raw_write_lock_bh include/linux/rwlock_api_smp.h:202 [inline] _raw_write_lock_bh+0x2f/0x40 kernel/locking/spinlock.c:334 ___neigh_create+0x9e1/0x2990 net/core/neighbour.c:652 ip6_finish_output2+0x1070/0x14f0 net/ipv6/ip6_output.c:123 __ip6_finish_output net/ipv6/ip6_output.c:191 [inline] __ip6_finish_output+0x61e/0xe90 net/ipv6/ip6_output.c:170 ip6_finish_output+0x32/0x200 net/ipv6/ip6_output.c:201 NF_HOOK_COND include/linux/netfilter.h:296 [inline] ip6_output+0x1e4/0x530 net/ipv6/ip6_output.c:224 dst_output include/net/dst.h:451 [inline] NF_HOOK include/linux/netfilter.h:307 [inline] ndisc_send_skb+0xa99/0x17f0 net/ipv6/ndisc.c:508 ndisc_send_ns+0x3a9/0x840 net/ipv6/ndisc.c:650 ndisc_solicit+0x2cd/0x4f0 net/ipv6/ndisc.c:742 neigh_probe+0xc2/0x110 net/core/neighbour.c:1040 __neigh_event_send+0x37d/0x1570 net/core/neighbour.c:1201 neigh_event_send include/net/neighbour.h:470 [inline] neigh_managed_work+0x162/0x250 net/core/neighbour.c:1574 process_one_work+0x9ac/0x1650 kernel/workqueue.c:2307 worker_thread+0x657/0x1110 kernel/workqueue.c:2454 kthread+0x2e9/0x3a0 kernel/kthread.c:377 ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:295 </TASK> Fixes: 7482e38 ("net, neigh: Add NTF_MANAGED flag for managed neighbor entries") Reported-by: syzbot+5239d0e1778a500d477a@syzkaller.appspotmail.com Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Cc: Eric Dumazet <edumazet@google.com> Cc: Roopa Prabhu <roopa@nvidia.com> Tested-by: syzbot+5239d0e1778a500d477a@syzkaller.appspotmail.com Reviewed-by: David Ahern <dsahern@kernel.org> Link: https://lore.kernel.org/r/20220201193942.5055-1-daniel@iogearbox.net Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Ivan Vecera <ivecera@redhat.com>
1 parent 651bc6a commit 28006e6

File tree

2 files changed

+25
-11
lines changed

2 files changed

+25
-11
lines changed

include/net/neighbour.h

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,8 @@ static inline struct neighbour *neigh_create(struct neigh_table *tbl,
339339
return __neigh_create(tbl, pkey, dev, true);
340340
}
341341
void neigh_destroy(struct neighbour *neigh);
342-
int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb);
342+
int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb,
343+
const bool immediate_ok);
343344
int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, u32 flags,
344345
u32 nlmsg_pid);
345346
void __neigh_set_probe_once(struct neighbour *neigh);
@@ -449,17 +450,24 @@ static inline struct neighbour * neigh_clone(struct neighbour *neigh)
449450

450451
#define neigh_hold(n) refcount_inc(&(n)->refcnt)
451452

452-
static inline int neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
453+
static __always_inline int neigh_event_send_probe(struct neighbour *neigh,
454+
struct sk_buff *skb,
455+
const bool immediate_ok)
453456
{
454457
unsigned long now = jiffies;
455-
458+
456459
if (READ_ONCE(neigh->used) != now)
457460
WRITE_ONCE(neigh->used, now);
458-
if (!(neigh->nud_state&(NUD_CONNECTED|NUD_DELAY|NUD_PROBE)))
459-
return __neigh_event_send(neigh, skb);
461+
if (!(neigh->nud_state & (NUD_CONNECTED | NUD_DELAY | NUD_PROBE)))
462+
return __neigh_event_send(neigh, skb, immediate_ok);
460463
return 0;
461464
}
462465

466+
static inline int neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
467+
{
468+
return neigh_event_send_probe(neigh, skb, true);
469+
}
470+
463471
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
464472
static inline int neigh_hh_bridge(struct hh_cache *hh, struct sk_buff *skb)
465473
{

net/core/neighbour.c

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,7 +1133,8 @@ static void neigh_timer_handler(struct timer_list *t)
11331133
neigh_release(neigh);
11341134
}
11351135

1136-
int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
1136+
int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb,
1137+
const bool immediate_ok)
11371138
{
11381139
int rc;
11391140
bool immediate_probe = false;
@@ -1154,12 +1155,17 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
11541155
atomic_set(&neigh->probes,
11551156
NEIGH_VAR(neigh->parms, UCAST_PROBES));
11561157
neigh_del_timer(neigh);
1157-
neigh->nud_state = NUD_INCOMPLETE;
1158+
neigh->nud_state = NUD_INCOMPLETE;
11581159
neigh->updated = now;
1159-
next = now + max(NEIGH_VAR(neigh->parms, RETRANS_TIME),
1160-
HZ/100);
1160+
if (!immediate_ok) {
1161+
next = now + 1;
1162+
} else {
1163+
immediate_probe = true;
1164+
next = now + max(NEIGH_VAR(neigh->parms,
1165+
RETRANS_TIME),
1166+
HZ / 100);
1167+
}
11611168
neigh_add_timer(neigh, next);
1162-
immediate_probe = true;
11631169
} else {
11641170
neigh->nud_state = NUD_FAILED;
11651171
neigh->updated = jiffies;
@@ -1571,7 +1577,7 @@ static void neigh_managed_work(struct work_struct *work)
15711577

15721578
write_lock_bh(&tbl->lock);
15731579
list_for_each_entry(neigh, &tbl->managed_list, managed_list)
1574-
neigh_event_send(neigh, NULL);
1580+
neigh_event_send_probe(neigh, NULL, false);
15751581
queue_delayed_work(system_power_efficient_wq, &tbl->managed_work,
15761582
NEIGH_VAR(&tbl->parms, DELAY_PROBE_TIME));
15771583
write_unlock_bh(&tbl->lock);

0 commit comments

Comments
 (0)