You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

302 lines
11 KiB

  1. commit 937604b4cfccddd607b8d4883815c4e3f9ab70d0
  2. Author: Willy Tarreau <w@1wt.eu>
  3. Date: Wed Jul 24 16:45:02 2019 +0200
  4. BUG/MEDIUM: protocols: add a global lock for the init/deinit stuff
  5. Dragan Dosen found that the listeners lock is not sufficient to protect
  6. the listeners list when proxies are stopping because the listeners are
  7. also unlinked from the protocol list, and under certain situations like
  8. bombing with soft-stop signals or shutting down many frontends in parallel
  9. from multiple CLI connections, it could be possible to provoke multiple
  10. instances of delete_listener() to be called in parallel for different
  11. listeners, thus corrupting the protocol lists.
  12. Such operations are pretty rare, they are performed once per proxy upon
  13. startup and once per proxy on shut down. Thus there is no point trying
  14. to optimize anything and we can use a global lock to protect the protocol
  15. lists during these manipulations.
  16. This fix (or a variant) will have to be backported as far as 1.8.
  17. (cherry picked from commit daacf3664506d56a1f3b050ccba504886a18b12a)
  18. Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
  19. diff --git a/include/proto/protocol.h b/include/proto/protocol.h
  20. index 7bbebb8e..f25f77f0 100644
  21. --- a/include/proto/protocol.h
  22. +++ b/include/proto/protocol.h
  23. @@ -23,9 +23,11 @@
  24. #define _PROTO_PROTOCOL_H
  25. #include <sys/socket.h>
  26. +#include <common/hathreads.h>
  27. #include <types/protocol.h>
  28. extern struct protocol *__protocol_by_family[AF_CUST_MAX];
  29. +__decl_hathreads(extern HA_SPINLOCK_T proto_lock);
  30. /* Registers the protocol <proto> */
  31. void protocol_register(struct protocol *proto);
  32. diff --git a/include/types/protocol.h b/include/types/protocol.h
  33. index 1d3404b9..f38baeb9 100644
  34. --- a/include/types/protocol.h
  35. +++ b/include/types/protocol.h
  36. @@ -80,9 +80,9 @@ struct protocol {
  37. int (*pause)(struct listener *l); /* temporarily pause this listener for a soft restart */
  38. void (*add)(struct listener *l, int port); /* add a listener for this protocol and port */
  39. - struct list listeners; /* list of listeners using this protocol */
  40. - int nb_listeners; /* number of listeners */
  41. - struct list list; /* list of registered protocols */
  42. + struct list listeners; /* list of listeners using this protocol (under proto_lock) */
  43. + int nb_listeners; /* number of listeners (under proto_lock) */
  44. + struct list list; /* list of registered protocols (under proto_lock) */
  45. };
  46. #define CONNECT_HAS_DATA 0x00000001 /* There's data available to be sent */
  47. diff --git a/src/listener.c b/src/listener.c
  48. index 40a774ed..b5fe2ac2 100644
  49. --- a/src/listener.c
  50. +++ b/src/listener.c
  51. @@ -433,6 +433,9 @@ static void limit_listener(struct listener *l, struct list *list)
  52. * used as a protocol's generic enable_all() primitive, for use after the
  53. * fork(). It puts the listeners into LI_READY or LI_FULL states depending on
  54. * their number of connections. It always returns ERR_NONE.
  55. + *
  56. + * Must be called with proto_lock held.
  57. + *
  58. */
  59. int enable_all_listeners(struct protocol *proto)
  60. {
  61. @@ -447,6 +450,9 @@ int enable_all_listeners(struct protocol *proto)
  62. * the polling lists when they are in the LI_READY or LI_FULL states. It is
  63. * intended to be used as a protocol's generic disable_all() primitive. It puts
  64. * the listeners into LI_LISTEN, and always returns ERR_NONE.
  65. + *
  66. + * Must be called with proto_lock held.
  67. + *
  68. */
  69. int disable_all_listeners(struct protocol *proto)
  70. {
  71. @@ -516,6 +522,9 @@ void unbind_listener_no_close(struct listener *listener)
  72. /* This function closes all listening sockets bound to the protocol <proto>,
  73. * and the listeners end in LI_ASSIGNED state if they were higher. It does not
  74. * detach them from the protocol. It always returns ERR_NONE.
  75. + *
  76. + * Must be called with proto_lock held.
  77. + *
  78. */
  79. int unbind_all_listeners(struct protocol *proto)
  80. {
  81. @@ -580,14 +589,19 @@ int create_listeners(struct bind_conf *bc, const struct sockaddr_storage *ss,
  82. * number of listeners is updated, as well as the global number of listeners
  83. * and jobs. Note that the listener must have previously been unbound. This
  84. * is the generic function to use to remove a listener.
  85. + *
  86. + * Will grab the proto_lock.
  87. + *
  88. */
  89. void delete_listener(struct listener *listener)
  90. {
  91. HA_SPIN_LOCK(LISTENER_LOCK, &listener->lock);
  92. if (listener->state == LI_ASSIGNED) {
  93. listener->state = LI_INIT;
  94. + HA_SPIN_LOCK(PROTO_LOCK, &proto_lock);
  95. LIST_DEL(&listener->proto_list);
  96. listener->proto->nb_listeners--;
  97. + HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock);
  98. _HA_ATOMIC_SUB(&jobs, 1);
  99. _HA_ATOMIC_SUB(&listeners, 1);
  100. }
  101. diff --git a/src/proto_sockpair.c b/src/proto_sockpair.c
  102. index a4faa370..e7dd670d 100644
  103. --- a/src/proto_sockpair.c
  104. +++ b/src/proto_sockpair.c
  105. @@ -80,6 +80,9 @@ INITCALL1(STG_REGISTER, protocol_register, &proto_sockpair);
  106. /* Add <listener> to the list of sockpair listeners (port is ignored). The
  107. * listener's state is automatically updated from LI_INIT to LI_ASSIGNED.
  108. * The number of listeners for the protocol is updated.
  109. + *
  110. + * Must be called with proto_lock held.
  111. + *
  112. */
  113. static void sockpair_add_listener(struct listener *listener, int port)
  114. {
  115. @@ -97,6 +100,8 @@ static void sockpair_add_listener(struct listener *listener, int port)
  116. * loose them across the fork(). A call to uxst_enable_listeners() is needed
  117. * to complete initialization.
  118. *
  119. + * Must be called with proto_lock held.
  120. + *
  121. * The return value is composed from ERR_NONE, ERR_RETRYABLE and ERR_FATAL.
  122. */
  123. static int sockpair_bind_listeners(struct protocol *proto, char *errmsg, int errlen)
  124. diff --git a/src/proto_tcp.c b/src/proto_tcp.c
  125. index 64ffb83c..bcbe27a7 100644
  126. --- a/src/proto_tcp.c
  127. +++ b/src/proto_tcp.c
  128. @@ -1103,6 +1103,9 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen)
  129. * The sockets will be registered but not added to any fd_set, in order not to
  130. * loose them across the fork(). A call to enable_all_listeners() is needed
  131. * to complete initialization. The return value is composed from ERR_*.
  132. + *
  133. + * Must be called with proto_lock held.
  134. + *
  135. */
  136. static int tcp_bind_listeners(struct protocol *proto, char *errmsg, int errlen)
  137. {
  138. @@ -1121,6 +1124,9 @@ static int tcp_bind_listeners(struct protocol *proto, char *errmsg, int errlen)
  139. /* Add <listener> to the list of tcpv4 listeners, on port <port>. The
  140. * listener's state is automatically updated from LI_INIT to LI_ASSIGNED.
  141. * The number of listeners for the protocol is updated.
  142. + *
  143. + * Must be called with proto_lock held.
  144. + *
  145. */
  146. static void tcpv4_add_listener(struct listener *listener, int port)
  147. {
  148. @@ -1136,6 +1142,9 @@ static void tcpv4_add_listener(struct listener *listener, int port)
  149. /* Add <listener> to the list of tcpv6 listeners, on port <port>. The
  150. * listener's state is automatically updated from LI_INIT to LI_ASSIGNED.
  151. * The number of listeners for the protocol is updated.
  152. + *
  153. + * Must be called with proto_lock held.
  154. + *
  155. */
  156. static void tcpv6_add_listener(struct listener *listener, int port)
  157. {
  158. diff --git a/src/proto_uxst.c b/src/proto_uxst.c
  159. index 66093af6..7263240f 100644
  160. --- a/src/proto_uxst.c
  161. +++ b/src/proto_uxst.c
  162. @@ -379,6 +379,9 @@ static int uxst_unbind_listener(struct listener *listener)
  163. /* Add <listener> to the list of unix stream listeners (port is ignored). The
  164. * listener's state is automatically updated from LI_INIT to LI_ASSIGNED.
  165. * The number of listeners for the protocol is updated.
  166. + *
  167. + * Must be called with proto_lock held.
  168. + *
  169. */
  170. static void uxst_add_listener(struct listener *listener, int port)
  171. {
  172. @@ -594,6 +597,8 @@ static int uxst_connect_server(struct connection *conn, int flags)
  173. * loose them across the fork(). A call to uxst_enable_listeners() is needed
  174. * to complete initialization.
  175. *
  176. + * Must be called with proto_lock held.
  177. + *
  178. * The return value is composed from ERR_NONE, ERR_RETRYABLE and ERR_FATAL.
  179. */
  180. static int uxst_bind_listeners(struct protocol *proto, char *errmsg, int errlen)
  181. @@ -613,6 +618,9 @@ static int uxst_bind_listeners(struct protocol *proto, char *errmsg, int errlen)
  182. /* This function stops all listening UNIX sockets bound to the protocol
  183. * <proto>. It does not detaches them from the protocol.
  184. * It always returns ERR_NONE.
  185. + *
  186. + * Must be called with proto_lock held.
  187. + *
  188. */
  189. static int uxst_unbind_listeners(struct protocol *proto)
  190. {
  191. diff --git a/src/protocol.c b/src/protocol.c
  192. index 96e01c82..ac45cf2e 100644
  193. --- a/src/protocol.c
  194. +++ b/src/protocol.c
  195. @@ -18,18 +18,26 @@
  196. #include <common/mini-clist.h>
  197. #include <common/standard.h>
  198. -#include <types/protocol.h>
  199. +#include <proto/protocol.h>
  200. /* List head of all registered protocols */
  201. static struct list protocols = LIST_HEAD_INIT(protocols);
  202. struct protocol *__protocol_by_family[AF_CUST_MAX] = { };
  203. +/* This is the global spinlock we may need to register/unregister listeners or
  204. + * protocols. Its main purpose is in fact to serialize the rare stop/deinit()
  205. + * phases.
  206. + */
  207. +__decl_spinlock(proto_lock);
  208. +
  209. /* Registers the protocol <proto> */
  210. void protocol_register(struct protocol *proto)
  211. {
  212. + HA_SPIN_LOCK(PROTO_LOCK, &proto_lock);
  213. LIST_ADDQ(&protocols, &proto->list);
  214. if (proto->sock_domain >= 0 && proto->sock_domain < AF_CUST_MAX)
  215. __protocol_by_family[proto->sock_domain] = proto;
  216. + HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock);
  217. }
  218. /* Unregisters the protocol <proto>. Note that all listeners must have
  219. @@ -37,8 +45,10 @@ void protocol_register(struct protocol *proto)
  220. */
  221. void protocol_unregister(struct protocol *proto)
  222. {
  223. + HA_SPIN_LOCK(PROTO_LOCK, &proto_lock);
  224. LIST_DEL(&proto->list);
  225. LIST_INIT(&proto->list);
  226. + HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock);
  227. }
  228. /* binds all listeners of all registered protocols. Returns a composition
  229. @@ -50,6 +60,7 @@ int protocol_bind_all(char *errmsg, int errlen)
  230. int err;
  231. err = 0;
  232. + HA_SPIN_LOCK(PROTO_LOCK, &proto_lock);
  233. list_for_each_entry(proto, &protocols, list) {
  234. if (proto->bind_all) {
  235. err |= proto->bind_all(proto, errmsg, errlen);
  236. @@ -57,6 +68,7 @@ int protocol_bind_all(char *errmsg, int errlen)
  237. break;
  238. }
  239. }
  240. + HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock);
  241. return err;
  242. }
  243. @@ -71,11 +83,13 @@ int protocol_unbind_all(void)
  244. int err;
  245. err = 0;
  246. + HA_SPIN_LOCK(PROTO_LOCK, &proto_lock);
  247. list_for_each_entry(proto, &protocols, list) {
  248. if (proto->unbind_all) {
  249. err |= proto->unbind_all(proto);
  250. }
  251. }
  252. + HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock);
  253. return err;
  254. }
  255. @@ -89,11 +103,13 @@ int protocol_enable_all(void)
  256. int err;
  257. err = 0;
  258. + HA_SPIN_LOCK(PROTO_LOCK, &proto_lock);
  259. list_for_each_entry(proto, &protocols, list) {
  260. if (proto->enable_all) {
  261. err |= proto->enable_all(proto);
  262. }
  263. }
  264. + HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock);
  265. return err;
  266. }
  267. @@ -107,11 +123,13 @@ int protocol_disable_all(void)
  268. int err;
  269. err = 0;
  270. + HA_SPIN_LOCK(PROTO_LOCK, &proto_lock);
  271. list_for_each_entry(proto, &protocols, list) {
  272. if (proto->disable_all) {
  273. err |= proto->disable_all(proto);
  274. }
  275. }
  276. + HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock);
  277. return err;
  278. }