commit 79765ba148ec6e8c9e91adfdf8d6b47e8c764bd8
parent e0be35c20d314b204d932e4093b29f0a27bda21c
Author: William Casarin <jb55@jb55.com>
Date: Tue, 8 Jul 2025 14:12:52 -0700
memory: fix a bunch of memory leaks
Signed-off-by: William Casarin <jb55@jb55.com>
Diffstat:
4 files changed, 44 insertions(+), 6 deletions(-)
diff --git a/Makefile b/Makefile
@@ -21,6 +21,8 @@ C_BINDINGS_COMMON=$(BINDINGS)/c/flatbuffers_common_builder.h $(BINDINGS)/c/flatb
C_BINDINGS=$(C_BINDINGS_COMMON) $(C_BINDINGS_PROFILE) $(C_BINDINGS_META)
BIN=ndb
+SANFLAGS = -fsanitize=leak
+
# Detect operating system
UNAME_S := $(shell uname -s)
@@ -189,6 +191,8 @@ testdata/db/.dir:
@mkdir -p testdata/db
touch testdata/db/.dir
+test: CFLAGS += $(SANFLAGS) # compile test objects with ASan/UBSan
+test: LDFLAGS += $(SANFLAGS) # link test binary with the sanitizer runtime
test: test.c $(DEPS) testdata/db/.dir
$(CC) $(CFLAGS) test.c $(LDS) $(LDFLAGS) -o $@
diff --git a/ndb.c b/ndb.c
@@ -387,7 +387,9 @@ int main(int argc, char *argv[])
}
ndb_end_query(&txn);
+ ndb_filter_destroy(f);
+ free(results);
} else if (argc == 3 && !strcmp(argv[1], "import")) {
if (!strcmp(argv[2], "-")) {
ndb_process_events_stream(ndb, stdin);
diff --git a/src/nostrdb.c b/src/nostrdb.c
@@ -591,6 +591,11 @@ int ndb_filter_end(struct ndb_filter *filter)
size_t orig_size;
#endif
size_t data_len, elem_len;
+ unsigned char *rel;
+
+ assert(filter);
+ assert(filter->elem_buf.start);
+
if (filter->finalized == 1)
return 0;
@@ -609,7 +614,10 @@ int ndb_filter_end(struct ndb_filter *filter)
memmove(filter->elem_buf.p, filter->data_buf.start, data_len);
// realloc the whole thing
- filter->elem_buf.start = realloc(filter->elem_buf.start, elem_len + data_len);
+ rel = realloc(filter->elem_buf.start, elem_len + data_len);
+ if (rel)
+ filter->elem_buf.start = rel;
+ assert(filter->elem_buf.start);
filter->elem_buf.end = filter->elem_buf.start + elem_len;
filter->elem_buf.p = filter->elem_buf.end;
@@ -4149,12 +4157,12 @@ static int ndb_query_plan_execute_profile_search(
// get the authors element after we finalize the filter, since
// the data could have moved
if (!(els = ndb_filter_find_elements(f, NDB_FILTER_AUTHORS)))
- return 0;
+ goto fail;
// grab pointer to pubkey in the filter so that we can
// update the filter as we go
if (!(filter_pubkey = ndb_filter_get_id_element(f, els, 0)))
- return 0;
+ goto fail;
for (i = 0; !query_is_full(results, limit); i++) {
if (i == 0) {
@@ -4170,10 +4178,15 @@ static int ndb_query_plan_execute_profile_search(
// Look up the corresponding note associated with that pubkey
if (!ndb_query_plan_execute_author_kinds(txn, f, results, limit))
- return 0;
+ goto fail;
}
+ ndb_filter_destroy(f);
return 1;
+
+fail:
+ ndb_filter_destroy(f);
+ return 0;
}
static int ndb_query_plan_execute_relay_kinds(
diff --git a/test.c b/test.c
@@ -233,6 +233,7 @@ static void test_timeline_query()
assert(ndb_query(&txn, &filter, 1, results,
sizeof(results)/sizeof(results[0]), &count));
ndb_end_query(&txn);
+ ndb_filter_destroy(&filter);
assert(count == 10);
}
@@ -291,6 +292,9 @@ static void test_fetched_at()
fetched_at = ndb_read_last_profile_fetch(&txn, pubkey);
assert(fetched_at == t1);
+
+ ndb_end_query(&txn);
+ ndb_destroy(ndb);
}
static void test_reaction_counter()
@@ -422,6 +426,7 @@ static void test_profile_updates()
assert(!strcmp(name, "c"));
+ ndb_end_query(&txn);
ndb_destroy(ndb);
free(json);
}
@@ -1413,6 +1418,7 @@ static void test_tag_query()
assert(!strcmp(ndb_note_content(results[0].note), "hi"));
ndb_end_query(&txn);
+ ndb_filter_destroy(f);
ndb_destroy(ndb);
}
@@ -1491,6 +1497,7 @@ static void test_query()
assert(!strcmp(ndb_note_content(results[1].note), "what"));
ndb_end_query(&txn);
+ ndb_filter_destroy(f);
ndb_destroy(ndb);
}
@@ -1614,6 +1621,7 @@ static void test_subscriptions()
assert(ndb_num_subscriptions(ndb) == 0);
ndb_end_query(&txn);
+ ndb_filter_destroy(f);
ndb_destroy(ndb);
}
@@ -1675,6 +1683,7 @@ static void test_weird_note_corruption() {
assert(i == 1);
ndb_end_query(&txn);
+ ndb_filter_destroy(f);
ndb_destroy(ndb);
}
@@ -1703,6 +1712,9 @@ static void test_filter_eq() {
ndb_filter_end(f2);
assert(ndb_filter_eq(f, f2));
+
+ ndb_filter_destroy(f);
+ ndb_filter_destroy(f2);
}
static void test_filter_is_subset() {
@@ -1738,6 +1750,9 @@ static void test_filter_is_subset() {
assert(ndb_filter_is_subset_of(k, g) == 1);
assert(ndb_filter_is_subset_of(ki, k) == 1);
assert(ndb_filter_is_subset_of(k, ki) == 0);
+
+ ndb_filter_destroy(g);
+ ndb_filter_destroy(ki);
}
static void test_filter_search()
@@ -1752,6 +1767,7 @@ static void test_filter_search()
ndb_filter_end_field(f);
assert(ndb_filter_end(f));
+ ndb_filter_destroy(f);
}
static void test_filter_parse_search_json() {
@@ -1784,6 +1800,8 @@ static void test_filter_parse_search_json() {
assert(ndb_filter_json(f, (char *)buf, sizeof(buf)));
printf("search json: '%s'\n", (const char *)buf);
assert(!strcmp((const char*)buf, json));
+
+ ndb_filter_destroy(f);
}
static void test_note_relay_index()
@@ -1903,6 +1921,7 @@ static void test_nip50_profile_search() {
assert(!memcmp(ndb_note_id(result.note), expected_id, 32));
// Cleanup
+ ndb_filter_destroy(f);
ndb_destroy(ndb);
printf("ok test_nip50_profile_search\n");
@@ -1954,7 +1973,7 @@ static void test_custom_filter()
struct ndb_filter filter, *f = &filter;
struct ndb_filter filter2, *f2 = &filter2;
int count, nres = 2;
- uint64_t sub_id, note_key;
+ uint64_t sub_id, note_keys[2];
struct ndb_query_result results[2];
struct ndb_ingest_meta meta;
@@ -1989,7 +2008,7 @@ static void test_custom_filter()
assert(ndb_process_event_with(ndb, reply_json, strlen(reply_json), &meta));
for (nres = 2; nres > 0;)
- nres -= ndb_wait_for_notes(ndb, sub_id, ¬e_key, 2);
+ nres -= ndb_wait_for_notes(ndb, sub_id, note_keys, 2);
ndb_begin_query(ndb, &txn);
ndb_query(&txn, f, 1, results, 2, &count);