Skip to content

Commit cfbfaf7

Browse files
committed
Fix memory leak when deleting a random entry that fits in a type3 (48)
node Add new unit test to verify no memory leaks with random delete of entries
1 parent cc014a3 commit cfbfaf7

File tree

3 files changed

+49
-5
lines changed

3 files changed

+49
-5
lines changed

src/art.c

+5-3
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ static void destroy_node(art_node *n) {
6868
}
6969

7070
// Handle each node type
71-
int i;
71+
int i, idx;
7272
union {
7373
art_node4 *p1;
7474
art_node16 *p2;
@@ -92,8 +92,10 @@ static void destroy_node(art_node *n) {
9292

9393
case NODE48:
9494
p.p3 = (art_node48*)n;
95-
for (i=0;i<n->num_children;i++) {
96-
destroy_node(p.p3->children[i]);
95+
for (i=0;i<256;i++) {
96+
idx = ((art_node48*)n)->keys[i];
97+
if (!idx) continue;
98+
destroy_node(p.p3->children[idx-1]);
9799
}
98100
break;
99101

tests/runner.c

+2
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ int main(void)
1919
tcase_add_test(tc1, test_art_insert_verylong);
2020
tcase_add_test(tc1, test_art_insert_search);
2121
tcase_add_test(tc1, test_art_insert_delete);
22+
tcase_add_test(tc1, test_art_insert_random_delete);
2223
tcase_add_test(tc1, test_art_insert_iter);
2324
tcase_add_test(tc1, test_art_iter_prefix);
2425
tcase_add_test(tc1, test_art_long_prefix);
2526
tcase_add_test(tc1, test_art_insert_search_uuid);
2627
tcase_add_test(tc1, test_art_max_prefix_len_scan_prefix);
28+
tcase_set_timeout(tc1, 180);
2729

2830
srunner_run_all(sr, CK_ENV);
2931
nf = srunner_ntests_failed(sr);

tests/test_art.c

+42-2
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,48 @@ START_TEST(test_art_insert_delete)
200200
}
201201
END_TEST
202202

203+
START_TEST(test_art_insert_random_delete)
204+
{
205+
art_tree t;
206+
int res = art_tree_init(&t);
207+
fail_unless(res == 0);
208+
209+
int len;
210+
char buf[512];
211+
FILE *f = fopen("tests/words.txt", "r");
212+
213+
uintptr_t line = 1;
214+
while (fgets(buf, sizeof buf, f)) {
215+
len = strlen(buf);
216+
buf[len-1] = '\0';
217+
fail_unless(NULL ==
218+
art_insert(&t, (unsigned char*)buf, len, (void*)line));
219+
line++;
220+
}
221+
222+
// Can be improved ensuring one delete on each node type
223+
// A is in 48 node
224+
uintptr_t lineno = 1;
225+
// Search first, ensure all entries are visible
226+
uintptr_t val = (uintptr_t)art_search(&t, (unsigned char*)"A", strlen("A")+1);
227+
fail_unless(lineno == val, "Line: %d Val: %" PRIuPTR " Str: %s\n", lineno,
228+
val, "A");
229+
230+
// Delete a single entry, should get lineno back
231+
val = (uintptr_t )art_delete(&t, (unsigned char *) "A", strlen("A")+1);
232+
fail_unless(lineno == val, "Line: %d Val: %" PRIuPTR " Str: %s\n", lineno,
233+
val, "A");
234+
235+
// Ensure the entry is no longer visible
236+
val = (uintptr_t)art_search(&t, (unsigned char*)"A", strlen("A")+1);
237+
fail_unless(0 == val, "Line: %d Val: %" PRIuPTR " Str: %s\n", 0,
238+
val, "A");
239+
240+
res = art_tree_destroy(&t);
241+
fail_unless(res == 0);
242+
}
243+
END_TEST
244+
203245
int iter_cb(void *data, const unsigned char* key, uint32_t key_len, void *val) {
204246
uint64_t *out = (uint64_t*)data;
205247
uintptr_t line = (uintptr_t)val;
@@ -448,5 +490,3 @@ START_TEST(test_art_max_prefix_len_scan_prefix)
448490
fail_unless(res == 0);
449491
}
450492
END_TEST
451-
452-

0 commit comments

Comments
 (0)