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

ocfs2: cluster: fix potential deadlock on &o2net_debug_lock

&o2net_debug_lock is acquired by timer o2net_idle_timer() along the
following call chain. Thus the acquisition of the lock under process
context should disable bottom half, otherwise deadlock could happen if the
timer happens to preempt the execution while the lock is held in process
context on the same CPU.

<timer interrupt>
-> o2net_idle_timer()
-> queue_delayed_work()
-> sc_put()
-> sc_kref_release()
-> o2net_debug_del_sc()
-> spin_lock(&o2net_debug_lock);

Several lock acquisition of &o2net_debug_lock under process context do not
disable irq or bottom half. The patch fixes these potential deadlocks
scenerio by using spin_lock_bh() on &o2net_debug_lock.

This flaw was found by an experimental static analysis tool I am
developing for irq-related deadlock. x86_64 allmodconfig using gcc shows
no new warning.

Link: https://lkml.kernel.org/r/20230802131436.17765-1-dg573847474@gmail.com
Signed-off-by: Chengfeng Ye <dg573847474@gmail.com>
Cc: Mark Fasheh <mark@fasheh.com>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: Junxiao Bi <junxiao.bi@oracle.com>
Cc: Joseph Qi <jiangqi903@gmail.com>
Cc: Gang He <ghe@suse.com>
Cc: Jun Piao <piaojun@huawei.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Chengfeng Ye and committed by
Andrew Morton
84c10951 28a45ef8

+20 -20
+20 -20
fs/ocfs2/cluster/netdebug.c
··· 44 44 45 45 void o2net_debug_add_nst(struct o2net_send_tracking *nst) 46 46 { 47 - spin_lock(&o2net_debug_lock); 47 + spin_lock_bh(&o2net_debug_lock); 48 48 list_add(&nst->st_net_debug_item, &send_tracking); 49 - spin_unlock(&o2net_debug_lock); 49 + spin_unlock_bh(&o2net_debug_lock); 50 50 } 51 51 52 52 void o2net_debug_del_nst(struct o2net_send_tracking *nst) 53 53 { 54 - spin_lock(&o2net_debug_lock); 54 + spin_lock_bh(&o2net_debug_lock); 55 55 if (!list_empty(&nst->st_net_debug_item)) 56 56 list_del_init(&nst->st_net_debug_item); 57 - spin_unlock(&o2net_debug_lock); 57 + spin_unlock_bh(&o2net_debug_lock); 58 58 } 59 59 60 60 static struct o2net_send_tracking ··· 84 84 { 85 85 struct o2net_send_tracking *nst, *dummy_nst = seq->private; 86 86 87 - spin_lock(&o2net_debug_lock); 87 + spin_lock_bh(&o2net_debug_lock); 88 88 nst = next_nst(dummy_nst); 89 - spin_unlock(&o2net_debug_lock); 89 + spin_unlock_bh(&o2net_debug_lock); 90 90 91 91 return nst; 92 92 } ··· 95 95 { 96 96 struct o2net_send_tracking *nst, *dummy_nst = seq->private; 97 97 98 - spin_lock(&o2net_debug_lock); 98 + spin_lock_bh(&o2net_debug_lock); 99 99 nst = next_nst(dummy_nst); 100 100 list_del_init(&dummy_nst->st_net_debug_item); 101 101 if (nst) 102 102 list_add(&dummy_nst->st_net_debug_item, 103 103 &nst->st_net_debug_item); 104 - spin_unlock(&o2net_debug_lock); 104 + spin_unlock_bh(&o2net_debug_lock); 105 105 106 106 return nst; /* unused, just needs to be null when done */ 107 107 } ··· 112 112 ktime_t now; 113 113 s64 sock, send, status; 114 114 115 - spin_lock(&o2net_debug_lock); 115 + spin_lock_bh(&o2net_debug_lock); 116 116 nst = next_nst(dummy_nst); 117 117 if (!nst) 118 118 goto out; ··· 145 145 (long long)status); 146 146 147 147 out: 148 - spin_unlock(&o2net_debug_lock); 148 + spin_unlock_bh(&o2net_debug_lock); 149 149 150 150 return 0; 151 151 } ··· 191 191 192 192 void o2net_debug_add_sc(struct o2net_sock_container *sc) 193 193 { 194 - spin_lock(&o2net_debug_lock); 194 + spin_lock_bh(&o2net_debug_lock); 195 195 list_add(&sc->sc_net_debug_item, &sock_containers); 196 - spin_unlock(&o2net_debug_lock); 196 + spin_unlock_bh(&o2net_debug_lock); 197 197 } 198 198 199 199 void o2net_debug_del_sc(struct o2net_sock_container *sc) 200 200 { 201 - spin_lock(&o2net_debug_lock); 201 + spin_lock_bh(&o2net_debug_lock); 202 202 list_del_init(&sc->sc_net_debug_item); 203 - spin_unlock(&o2net_debug_lock); 203 + spin_unlock_bh(&o2net_debug_lock); 204 204 } 205 205 206 206 struct o2net_sock_debug { ··· 236 236 struct o2net_sock_debug *sd = seq->private; 237 237 struct o2net_sock_container *sc, *dummy_sc = sd->dbg_sock; 238 238 239 - spin_lock(&o2net_debug_lock); 239 + spin_lock_bh(&o2net_debug_lock); 240 240 sc = next_sc(dummy_sc); 241 - spin_unlock(&o2net_debug_lock); 241 + spin_unlock_bh(&o2net_debug_lock); 242 242 243 243 return sc; 244 244 } ··· 248 248 struct o2net_sock_debug *sd = seq->private; 249 249 struct o2net_sock_container *sc, *dummy_sc = sd->dbg_sock; 250 250 251 - spin_lock(&o2net_debug_lock); 251 + spin_lock_bh(&o2net_debug_lock); 252 252 sc = next_sc(dummy_sc); 253 253 list_del_init(&dummy_sc->sc_net_debug_item); 254 254 if (sc) 255 255 list_add(&dummy_sc->sc_net_debug_item, &sc->sc_net_debug_item); 256 - spin_unlock(&o2net_debug_lock); 256 + spin_unlock_bh(&o2net_debug_lock); 257 257 258 258 return sc; /* unused, just needs to be null when done */ 259 259 } ··· 349 349 struct o2net_sock_debug *sd = seq->private; 350 350 struct o2net_sock_container *sc, *dummy_sc = sd->dbg_sock; 351 351 352 - spin_lock(&o2net_debug_lock); 352 + spin_lock_bh(&o2net_debug_lock); 353 353 sc = next_sc(dummy_sc); 354 354 355 355 if (sc) { ··· 359 359 sc_show_sock_stats(seq, sc); 360 360 } 361 361 362 - spin_unlock(&o2net_debug_lock); 362 + spin_unlock_bh(&o2net_debug_lock); 363 363 364 364 return 0; 365 365 }