]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MEDIUM: leastconn: fix rare possibility of divide by zero
authorWilly Tarreau <w@1wt.eu>
Wed, 22 Sep 2021 05:15:57 +0000 (07:15 +0200)
committerWilly Tarreau <w@1wt.eu>
Wed, 22 Sep 2021 05:24:02 +0000 (07:24 +0200)
An optimization was brought in commit 5064ab6a9 ("OPTIM: lb-leastconn:
do not unlink the server if it did not change") to avoid locking the
server just to discover it did not move. However a mistake was made
because the operation involves a divide with a value that is read
outside of its usual lock, which makes it possible to be zero at the
exact moment we watch it if another thread takes the server down under
the lbprm lock, resulting in a divide by zero.

Therefore we must check that the value is not null there.

This must be backported to 2.4.

src/lb_fwlc.c

index 091241cc5b8704866b7179c24cc3fcb40dbab274..8e913d48633374c4cf140a526765857e6cb5b52b 100644 (file)
@@ -71,12 +71,13 @@ static inline void fwlc_queue_srv(struct server *s, unsigned int eweight)
 static void fwlc_srv_reposition(struct server *s)
 {
        unsigned int inflight = _HA_ATOMIC_LOAD(&s->served) + _HA_ATOMIC_LOAD(&s->queue.length);
-       unsigned int new_key = inflight ? (inflight + 1) * SRV_EWGHT_MAX / s->cur_eweight : 0;
+       unsigned int eweight = _HA_ATOMIC_LOAD(&s->cur_eweight);
+       unsigned int new_key = inflight ? (inflight + 1) * SRV_EWGHT_MAX / (eweight ? eweight : 1) : 0;
 
        /* some calls will be made for no change (e.g connect_server() after
         * assign_server(). Let's check that first.
         */
-       if (s->lb_node.node.leaf_p && s->lb_node.key == new_key)
+       if (s->lb_node.node.leaf_p && eweight && s->lb_node.key == new_key)
                return;
 
        HA_RWLOCK_WRLOCK(LBPRM_LOCK, &s->proxy->lbprm.lock);
@@ -87,7 +88,8 @@ static void fwlc_srv_reposition(struct server *s)
                 * to our target value (50% of the case in measurements).
                 */
                inflight = _HA_ATOMIC_LOAD(&s->served) + _HA_ATOMIC_LOAD(&s->queue.length);
-               new_key = inflight ? (inflight + 1) * SRV_EWGHT_MAX / s->cur_eweight : 0;
+               eweight = _HA_ATOMIC_LOAD(&s->cur_eweight);
+               new_key = inflight ? (inflight + 1) * SRV_EWGHT_MAX / (eweight ? eweight : 1) : 0;
                if (!s->lb_node.node.leaf_p || s->lb_node.key != new_key) {
                        eb32_delete(&s->lb_node);
                        s->lb_node.key = new_key;