1 commit b10c8d7641cc8ceae6fba4506b7f987d66109bd9
2 Author: Willy Tarreau <w@1wt.eu>
3 Date: Mon Aug 26 10:55:52 2019 +0200
5 BUG/MEDIUM: listener/threads: fix an AB/BA locking issue in delete_listener()
7 The delete_listener() function takes the listener's lock before taking
8 the proto_lock, which is contrary to what other functions do, possibly
9 causing an AB/BA deadlock. In practice the two only places where both
10 are taken are during protocol_enable_all() and delete_listener(), the
11 former being used during startup and the latter during stop. In practice
12 during reload floods, it is technically possible for a thread to be
13 initializing the listeners while another one is stopping. While this
14 is too hard to trigger on 2.0 and above due to the synchronization of
15 all threads during startup, it's reasonably easy to do in 1.9 by having
16 hundreds of listeners, starting 64 threads and flooding them with reloads
19 $ while usleep 50000; do killall -USR2 haproxy; done
21 Usually in less than a minute, all threads will be deadlocked. The fix
22 consists in always taking the proto_lock before the listener lock. It
23 seems to be the only place where these two locks were reversed. This
24 fix needs to be backported to 2.0, 1.9, and 1.8.
26 (cherry picked from commit 6ee9f8df3bfbb811526cff3313da5758b1277bc6)
27 Signed-off-by: Willy Tarreau <w@1wt.eu>
29 diff --git a/src/listener.c b/src/listener.c
30 index b5fe2ac2..54c09960 100644
33 @@ -595,17 +595,17 @@ int create_listeners(struct bind_conf *bc, const struct sockaddr_storage *ss,
35 void delete_listener(struct listener *listener)
37 + HA_SPIN_LOCK(PROTO_LOCK, &proto_lock);
38 HA_SPIN_LOCK(LISTENER_LOCK, &listener->lock);
39 if (listener->state == LI_ASSIGNED) {
40 listener->state = LI_INIT;
41 - HA_SPIN_LOCK(PROTO_LOCK, &proto_lock);
42 LIST_DEL(&listener->proto_list);
43 listener->proto->nb_listeners--;
44 - HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock);
45 _HA_ATOMIC_SUB(&jobs, 1);
46 _HA_ATOMIC_SUB(&listeners, 1);
48 HA_SPIN_UNLOCK(LISTENER_LOCK, &listener->lock);
49 + HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock);
52 /* Returns a suitable value for a listener's backlog. It uses the listener's,