diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2024-10-30 13:18:26 -0400 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2024-11-02 11:25:10 -0400 |
commit | 789e19f9dca87780aff3485d4a120d8b4ddedfb1 (patch) | |
tree | 4b95a7b02632aa7d8307f5a670ac78e19a50e994 | |
parent | c90d4af8750bef77ce9abe91df80d5d98ba297d7 (diff) | |
download | bfs-789e19f9dca87780aff3485d4a120d8b4ddedfb1.tar.xz |
trie: Fix varena_free() with wrong size
In trie_remove(), clearing the bit before trie_node_collapse() causes us
to free the old node with size 1 instead of 2, putting it on the wrong
freelist. This is technically safe with the current arena
implementation, but not intentional.
-rw-r--r-- | src/trie.c | 16 |
1 files changed, 8 insertions, 8 deletions
@@ -670,7 +670,7 @@ static int trie_collapse_node(struct trie *trie, uintptr_t *parent, struct trie_ } *parent = other; - trie_node_free(trie, parent_node, 1); + trie_node_free(trie, parent_node, 2); return 0; } @@ -709,19 +709,19 @@ static void trie_remove_impl(struct trie *trie, struct trie_leaf *leaf) { } struct trie_node *node = trie_decode_node(*parent); - child = node->children + child_index; - trie_free_singletons(trie, *child); + trie_free_singletons(trie, node->children[child_index]); - node->bitmap ^= child_bit; unsigned int parent_size = count_ones(node->bitmap); - bfs_assert(parent_size > 0); - if (parent_size == 1 && trie_collapse_node(trie, parent, node, child_index) == 0) { + bfs_assert(parent_size > 1); + if (parent_size == 2 && trie_collapse_node(trie, parent, node, child_index) == 0) { return; } - if (child_index < parent_size) { - memmove(child, child + 1, (parent_size - child_index) * sizeof(*child)); + for (size_t i = child_index; i + 1 < parent_size; ++i) { + node->children[i] = node->children[i + 1]; } + node->bitmap &= ~child_bit; + --parent_size; if (has_single_bit(parent_size)) { node = trie_node_realloc(trie, node, 2 * parent_size, parent_size); |