]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MEDIUM: lb-chash: Ensure the tree integrity when server weight is increased
authorChristopher Faulet <cfaulet@haproxy.com>
Thu, 1 Aug 2019 08:09:29 +0000 (10:09 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Thu, 1 Aug 2019 09:35:29 +0000 (11:35 +0200)
When the server weight is increased in consistant hash, extra nodes have to be
allocated. So a realloc() is performed on the nodes array of the server. the
previous commit 962ea7732 ("BUG/MEDIUM: lb-chash: Remove all server's entries
before realloc() to re-insert them after") have fixed the size used during the
realloc() to avoid segfaults. But another bug remains. After the realloc(), the
memory area allocated for the nodes array may change, invalidating all node
addresses in the chash tree.

So, to fix the bug, we must remove all server's entries from the chash tree
before the realloc to insert all of them after, old nodes and new ones. The
insert will be automatically handled by the loop at the end of the function
chash_queue_dequeue_srv().

Note that if the call to realloc() failed, no new entries will be created for
the server, so the effective server weight will be unchanged.

This issue was reported on Github (#189).

This patch must be backported to all versions since the 1.6.

src/lb_chash.c

index 0bf4e81a2c9a45c6ae0f8f146e674b0c08bec8ac..23448df80a799bd2ab0c33fc71afaefb054b4f79 100644 (file)
@@ -84,8 +84,13 @@ static inline void chash_queue_dequeue_srv(struct server *s)
         * increased the weight beyond the original weight
         */
        if (s->lb_nodes_tot < s->next_eweight) {
-               struct tree_occ *new_nodes = realloc(s->lb_nodes, s->next_eweight * sizeof(*new_nodes));
+               struct tree_occ *new_nodes;
 
+               /* First we need to remove all server's entries from its tree
+                * because the realloc will change all nodes pointers */
+               chash_dequeue_srv(s);
+
+               new_nodes = realloc(s->lb_nodes, s->next_eweight * sizeof(*new_nodes));
                if (new_nodes) {
                        unsigned int j;
 
@@ -494,7 +499,6 @@ void chash_init_server_tree(struct proxy *p)
                srv->lb_nodes_tot = srv->uweight * BE_WEIGHT_SCALE;
                srv->lb_nodes_now = 0;
                srv->lb_nodes = calloc(srv->lb_nodes_tot, sizeof(struct tree_occ));
-
                for (node = 0; node < srv->lb_nodes_tot; node++) {
                        srv->lb_nodes[node].server = srv;
                        srv->lb_nodes[node].node.key = full_hash(srv->puid * SRV_EWGHT_RANGE + node);