]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[BUG] ebtree: fix duplicate strings insertion
authorWilly Tarreau <w@1wt.eu>
Thu, 28 Oct 2010 20:48:29 +0000 (22:48 +0200)
committerWilly Tarreau <w@1wt.eu>
Sat, 30 Oct 2010 17:04:36 +0000 (19:04 +0200)
(update to ebtree 6.0.4)

Recent fix fd301cc1370cd4977fe175dfa4544c7dc0e7ce6b was not OK because it
was returning one excess byte, causing some duplicates not to be detected.
The reason is that we added 8 bits to count the trailing zero but they
were implied by the pre-incrementation of the pointer.

Fixing this was still not enough, as the problem appeared when
string_equal_bits() was applied on two identical strings, and it returned
a number of bits covering the trailing zero. Subsequent calls were applied
to the first byte after this trailing zero. It was often zero when doing
insertion from raw files, explaining why the issue was not discovered
earlier. But when the data is from a reused area, duplicate strings are not
correctly detected when inserting into the tree.

Several solutions were tested, and the only efficient one consists in making
string_equal_bits() notify the caller that the end of the string was reached.
It now returns zero and the callers just have to ensure that when they get a
zero, they stop using that bit until a dup tree or a leaf is encountered.

This fix brought the unexpected bonus of simplifying the insertion code a bit
and making it slightly faster to process duplicates.

The impact for haproxy was that if many similar string patterns were loaded
from a file, there was a potential risk that their insertion or matching could
have been slower. The bigger impact was with the URL sorting feature of halog,
which is not yet merged and is how this bug was discovered.
(cherry picked from commit 518d59ec9ba43705f930f9ece3749c450fd005df)

ebtree/ebistree.h
ebtree/ebsttree.h
ebtree/ebtree.h

index b77c74899c484d8df24ca7d0519455c047e5748f..a82130aa36836b2ef83ed45d29c583f802a7beda 100644 (file)
@@ -80,10 +80,24 @@ static forceinline struct ebpt_node *__ebis_lookup(struct eb_root *root, const v
                        return node;
                }
 
-               /* OK, normal data node, let's walk down */
-               bit = string_equal_bits(x, node->key, bit);
-               if (bit < node_bit)
-                       return NULL; /* no more common bits */
+               /* OK, normal data node, let's walk down but don't compare data
+                * if we already reached the end of the key.
+                */
+               if (likely(bit >= 0)) {
+                       bit = string_equal_bits(x, node->key, bit);
+                       if (likely(bit < node_bit)) {
+                               if (bit >= 0)
+                                       return NULL; /* no more common bits */
+
+                               /* bit < 0 : we reached the end of the key. If we
+                                * are in a tree with unique keys, we can return
+                                * this node. Otherwise we have to walk it down
+                                * and stop comparing bits.
+                                */
+                               if (eb_gettag(root->b[EB_RGHT]))
+                                       return node;
+                       }
+               }
 
                troot = node->node.branches.b[(((unsigned char*)x)[node_bit >> 3] >>
                                               (~node_bit & 7)) & 1];
@@ -160,32 +174,41 @@ __ebis_insert(struct eb_root *root, struct ebpt_node *new)
                         *
                         * The last two cases can easily be partially merged.
                         */
-                       bit = string_equal_bits(new->key, old->key, bit);
-                       diff = cmp_bits(new->key, old->key, bit);
+                       if (bit >= 0)
+                               bit = string_equal_bits(new->key, old->key, bit);
+
+                       if (bit < 0) {
+                               /* key was already there */
 
-                       if (diff < 0) {
-                               new->node.leaf_p = new_left;
-                               old->node.leaf_p = new_rght;
-                               new->node.branches.b[EB_LEFT] = new_leaf;
-                               new->node.branches.b[EB_RGHT] = old_leaf;
-                       } else {
                                /* we may refuse to duplicate this key if the tree is
                                 * tagged as containing only unique keys.
                                 */
-                               if (diff == 0 && eb_gettag(root_right))
+                               if (eb_gettag(root_right))
                                        return old;
 
-                               /* new->key >= old->key, new goes the right */
+                               /* new arbitrarily goes to the right and tops the dup tree */
                                old->node.leaf_p = new_left;
                                new->node.leaf_p = new_rght;
                                new->node.branches.b[EB_LEFT] = old_leaf;
                                new->node.branches.b[EB_RGHT] = new_leaf;
+                               new->node.bit = -1;
+                               root->b[side] = eb_dotag(&new->node.branches, EB_NODE);
+                               return new;
+                       }
 
-                               if (diff == 0) {
-                                       new->node.bit = -1;
-                                       root->b[side] = eb_dotag(&new->node.branches, EB_NODE);
-                                       return new;
-                               }
+                       diff = cmp_bits(new->key, old->key, bit);
+                       if (diff < 0) {
+                               /* new->key < old->key, new takes the left */
+                               new->node.leaf_p = new_left;
+                               old->node.leaf_p = new_rght;
+                               new->node.branches.b[EB_LEFT] = new_leaf;
+                               new->node.branches.b[EB_RGHT] = old_leaf;
+                       } else {
+                               /* new->key > old->key, new takes the right */
+                               old->node.leaf_p = new_left;
+                               new->node.leaf_p = new_rght;
+                               new->node.branches.b[EB_LEFT] = old_leaf;
+                               new->node.branches.b[EB_RGHT] = new_leaf;
                        }
                        break;
                }
@@ -201,23 +224,36 @@ __ebis_insert(struct eb_root *root, struct ebpt_node *new)
                 * the current node's because as long as they are identical, we
                 * know we descend along the correct side.
                 */
-               if (old_node_bit < 0) {
-                       /* we're above a duplicate tree, we must compare till the end */
-                       bit = string_equal_bits(new->key, old->key, bit);
-                       goto dup_tree;
-               }
-               else if (bit < old_node_bit) {
+               if (bit >= 0 && (bit < old_node_bit || old_node_bit < 0))
                        bit = string_equal_bits(new->key, old->key, bit);
-               }
 
-               if (bit < old_node_bit) { /* we don't have all bits in common */
-                       /* The tree did not contain the key, so we insert <new> before the node
-                        * <old>, and set ->bit to designate the lowest bit position in <new>
-                        * which applies to ->branches.b[].
+               if (unlikely(bit < 0)) {
+                       /* Perfect match, we must only stop on head of dup tree
+                        * or walk down to a leaf.
+                        */
+                       if (old_node_bit < 0) {
+                               /* We know here that string_equal_bits matched all
+                                * bits and that we're on top of a dup tree, then
+                                * we can perform the dup insertion and return.
+                                */
+                               struct eb_node *ret;
+                               ret = eb_insert_dup(&old->node, &new->node);
+                               return container_of(ret, struct ebpt_node, node);
+                       }
+                       /* OK so let's walk down */
+               }
+               else if (bit < old_node_bit || old_node_bit < 0) {
+                       /* The tree did not contain the key, or we stopped on top of a dup
+                        * tree, possibly containing the key. In the former case, we insert
+                        * <new> before the node <old>, and set ->bit to designate the lowest
+                        * bit position in <new> which applies to ->branches.b[]. In the later
+                        * case, we add the key to the existing dup tree. Note that we cannot
+                        * enter here if we match an intermediate node's key that is not the
+                        * head of a dup tree.
                         */
                        eb_troot_t *new_left, *new_rght;
                        eb_troot_t *new_leaf, *old_node;
-               dup_tree:
+
                        new_left = eb_dotag(&new->node.branches, EB_LEFT);
                        new_rght = eb_dotag(&new->node.branches, EB_RGHT);
                        new_leaf = eb_dotag(&new->node.branches, EB_LEAF);
@@ -225,6 +261,7 @@ __ebis_insert(struct eb_root *root, struct ebpt_node *new)
 
                        new->node.node_p = old->node.node_p;
 
+                       /* we can never match all bits here */
                        diff = cmp_bits(new->key, old->key, bit);
                        if (diff < 0) {
                                new->node.leaf_p = new_left;
@@ -232,17 +269,12 @@ __ebis_insert(struct eb_root *root, struct ebpt_node *new)
                                new->node.branches.b[EB_LEFT] = new_leaf;
                                new->node.branches.b[EB_RGHT] = old_node;
                        }
-                       else if (diff > 0) {
+                       else {
                                old->node.node_p = new_left;
                                new->node.leaf_p = new_rght;
                                new->node.branches.b[EB_LEFT] = old_node;
                                new->node.branches.b[EB_RGHT] = new_leaf;
                        }
-                       else {
-                               struct eb_node *ret;
-                               ret = eb_insert_dup(&old->node, &new->node);
-                               return container_of(ret, struct ebpt_node, node);
-                       }
                        break;
                }
 
@@ -260,6 +292,7 @@ __ebis_insert(struct eb_root *root, struct ebpt_node *new)
 
        /* We need the common higher bits between new->key and old->key.
         * This number of bits is already in <bit>.
+        * NOTE: we can't get here whit bit < 0 since we found a dup !
         */
        new->node.bit = bit;
        root->b[side] = eb_dotag(&new->node.branches, EB_NODE);
index f15af38fde801f5ecd04bb21447ac6ae1052e9a5..5c52d6e94d0b7a773045fb2d49b4cc0ee0733864 100644 (file)
@@ -78,10 +78,24 @@ static forceinline struct ebmb_node *__ebst_lookup(struct eb_root *root, const v
                        return node;
                }
 
-               /* OK, normal data node, let's walk down */
-               bit = string_equal_bits(x, node->key, bit);
-               if (bit < node_bit)
-                       return NULL; /* no more common bits */
+               /* OK, normal data node, let's walk down but don't compare data
+                * if we already reached the end of the key.
+                */
+               if (likely(bit >= 0)) {
+                       bit = string_equal_bits(x, node->key, bit);
+                       if (likely(bit < node_bit)) {
+                               if (bit >= 0)
+                                       return NULL; /* no more common bits */
+
+                               /* bit < 0 : we reached the end of the key. If we
+                                * are in a tree with unique keys, we can return
+                                * this node. Otherwise we have to walk it down
+                                * and stop comparing bits.
+                                */
+                               if (eb_gettag(root->b[EB_RGHT]))
+                                       return node;
+                       }
+               }
 
                troot = node->node.branches.b[(((unsigned char*)x)[node_bit >> 3] >>
                                               (~node_bit & 7)) & 1];
@@ -158,32 +172,41 @@ __ebst_insert(struct eb_root *root, struct ebmb_node *new)
                         *
                         * The last two cases can easily be partially merged.
                         */
-                       bit = string_equal_bits(new->key, old->key, bit);
-                       diff = cmp_bits(new->key, old->key, bit);
+                       if (bit >= 0)
+                               bit = string_equal_bits(new->key, old->key, bit);
+
+                       if (bit < 0) {
+                               /* key was already there */
 
-                       if (diff < 0) {
-                               new->node.leaf_p = new_left;
-                               old->node.leaf_p = new_rght;
-                               new->node.branches.b[EB_LEFT] = new_leaf;
-                               new->node.branches.b[EB_RGHT] = old_leaf;
-                       } else {
                                /* we may refuse to duplicate this key if the tree is
                                 * tagged as containing only unique keys.
                                 */
-                               if (diff == 0 && eb_gettag(root_right))
+                               if (eb_gettag(root_right))
                                        return old;
 
-                               /* new->key >= old->key, new goes the right */
+                               /* new arbitrarily goes to the right and tops the dup tree */
                                old->node.leaf_p = new_left;
                                new->node.leaf_p = new_rght;
                                new->node.branches.b[EB_LEFT] = old_leaf;
                                new->node.branches.b[EB_RGHT] = new_leaf;
+                               new->node.bit = -1;
+                               root->b[side] = eb_dotag(&new->node.branches, EB_NODE);
+                               return new;
+                       }
 
-                               if (diff == 0) {
-                                       new->node.bit = -1;
-                                       root->b[side] = eb_dotag(&new->node.branches, EB_NODE);
-                                       return new;
-                               }
+                       diff = cmp_bits(new->key, old->key, bit);
+                       if (diff < 0) {
+                               /* new->key < old->key, new takes the left */
+                               new->node.leaf_p = new_left;
+                               old->node.leaf_p = new_rght;
+                               new->node.branches.b[EB_LEFT] = new_leaf;
+                               new->node.branches.b[EB_RGHT] = old_leaf;
+                       } else {
+                               /* new->key > old->key, new takes the right */
+                               old->node.leaf_p = new_left;
+                               new->node.leaf_p = new_rght;
+                               new->node.branches.b[EB_LEFT] = old_leaf;
+                               new->node.branches.b[EB_RGHT] = new_leaf;
                        }
                        break;
                }
@@ -199,23 +222,36 @@ __ebst_insert(struct eb_root *root, struct ebmb_node *new)
                 * the current node's because as long as they are identical, we
                 * know we descend along the correct side.
                 */
-               if (old_node_bit < 0) {
-                       /* we're above a duplicate tree, we must compare till the end */
-                       bit = string_equal_bits(new->key, old->key, bit);
-                       goto dup_tree;
-               }
-               else if (bit < old_node_bit) {
+               if (bit >= 0 && (bit < old_node_bit || old_node_bit < 0))
                        bit = string_equal_bits(new->key, old->key, bit);
-               }
 
-               if (bit < old_node_bit) { /* we don't have all bits in common */
-                       /* The tree did not contain the key, so we insert <new> before the node
-                        * <old>, and set ->bit to designate the lowest bit position in <new>
-                        * which applies to ->branches.b[].
+               if (unlikely(bit < 0)) {
+                       /* Perfect match, we must only stop on head of dup tree
+                        * or walk down to a leaf.
+                        */
+                       if (old_node_bit < 0) {
+                               /* We know here that string_equal_bits matched all
+                                * bits and that we're on top of a dup tree, then
+                                * we can perform the dup insertion and return.
+                                */
+                               struct eb_node *ret;
+                               ret = eb_insert_dup(&old->node, &new->node);
+                               return container_of(ret, struct ebmb_node, node);
+                       }
+                       /* OK so let's walk down */
+               }
+               else if (bit < old_node_bit || old_node_bit < 0) {
+                       /* The tree did not contain the key, or we stopped on top of a dup
+                        * tree, possibly containing the key. In the former case, we insert
+                        * <new> before the node <old>, and set ->bit to designate the lowest
+                        * bit position in <new> which applies to ->branches.b[]. In the later
+                        * case, we add the key to the existing dup tree. Note that we cannot
+                        * enter here if we match an intermediate node's key that is not the
+                        * head of a dup tree.
                         */
                        eb_troot_t *new_left, *new_rght;
                        eb_troot_t *new_leaf, *old_node;
-               dup_tree:
+
                        new_left = eb_dotag(&new->node.branches, EB_LEFT);
                        new_rght = eb_dotag(&new->node.branches, EB_RGHT);
                        new_leaf = eb_dotag(&new->node.branches, EB_LEAF);
@@ -223,6 +259,7 @@ __ebst_insert(struct eb_root *root, struct ebmb_node *new)
 
                        new->node.node_p = old->node.node_p;
 
+                       /* we can never match all bits here */
                        diff = cmp_bits(new->key, old->key, bit);
                        if (diff < 0) {
                                new->node.leaf_p = new_left;
@@ -230,17 +267,12 @@ __ebst_insert(struct eb_root *root, struct ebmb_node *new)
                                new->node.branches.b[EB_LEFT] = new_leaf;
                                new->node.branches.b[EB_RGHT] = old_node;
                        }
-                       else if (diff > 0) {
+                       else {
                                old->node.node_p = new_left;
                                new->node.leaf_p = new_rght;
                                new->node.branches.b[EB_LEFT] = old_node;
                                new->node.branches.b[EB_RGHT] = new_leaf;
                        }
-                       else {
-                               struct eb_node *ret;
-                               ret = eb_insert_dup(&old->node, &new->node);
-                               return container_of(ret, struct ebmb_node, node);
-                       }
                        break;
                }
 
@@ -258,6 +290,7 @@ __ebst_insert(struct eb_root *root, struct ebmb_node *new)
 
        /* We need the common higher bits between new->key and old->key.
         * This number of bits is already in <bit>.
+        * NOTE: we can't get here whit bit < 0 since we found a dup !
         */
        new->node.bit = bit;
        root->b[side] = eb_dotag(&new->node.branches, EB_NODE);
index 5acbbcfc4331c5ece2c0281ec4e8ecd7f3f1b85d..e0f9a18127b5d16e9fcc08bf89265192318f5bfb 100644 (file)
@@ -793,8 +793,8 @@ static forceinline int check_bits(const unsigned char *a,
  * may be rechecked. It is only passed here as a hint to speed up the check.
  * The caller is responsible for not passing an <ignore> value larger than any
  * of the two strings. However, referencing any bit from the trailing zero is
- * permitted. Equal strings are reported as equal up to and including the last
- * zero.
+ * permitted. Equal strings are reported as a negative number of bits, which
+ * indicates the end was reached.
  */
 static forceinline int string_equal_bits(const unsigned char *a,
                                         const unsigned char *b,
@@ -819,7 +819,7 @@ static forceinline int string_equal_bits(const unsigned char *a,
                if (c)
                        break;
                if (!d)
-                       return (beg << 3) + 8; /* equal bytes + zero */
+                       return -1;
        }
        /* OK now we know that a and b differ at byte <beg>, or that both are zero.
         * We have to find what bit is differing and report it as the number of