Skip to content

Commit c9002a5

Browse files
fix(cross-repo): harden Maven library cleanup and growth
1 parent 4bf45f8 commit c9002a5

3 files changed

Lines changed: 98 additions & 14 deletions

File tree

src/pipeline/pass_cross_repo.c

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,16 +114,14 @@ static void delete_cross_edges(cbm_store_t *store, const char *project) {
114114
cbm_store_delete_edges_by_type(store, project, "CROSS_GRAPHQL_CALLS");
115115
cbm_store_delete_edges_by_type(store, project, "CROSS_TRPC_CALLS");
116116
cbm_store_delete_edges_by_type(store, project, "CROSS_LIBRARY_DEPENDS_ON");
117-
cbm_store_delete_edges_by_type(store, project, "CROSS_LIBRARY_USED_BY");
118117
struct sqlite3 *db = cbm_store_get_db(store);
119118
if (!db) {
120119
return;
121120
}
122121
sqlite3_stmt *st = NULL;
123122
if (sqlite3_prepare_v2(db,
124-
"DELETE FROM nodes WHERE project=?1 AND "
125-
"(qualified_name GLOB '__library__*' OR "
126-
"qualified_name GLOB '__library_consumer__*')",
123+
"DELETE FROM nodes WHERE project=?1 AND label='Library' "
124+
"AND qualified_name GLOB '__library__*'",
127125
CBM_NOT_FOUND, &st, NULL) == SQLITE_OK) {
128126
sqlite3_bind_text(st, SKIP_ONE, project, CBM_NOT_FOUND, SQLITE_STATIC);
129127
sqlite3_step(st);
@@ -593,15 +591,25 @@ static int collect_all_projects(char ***out) {
593591
continue;
594592
}
595593
if (count >= cap) {
596-
cap *= PAIR_LEN;
597-
char **tmp = realloc(projects, (size_t)cap * sizeof(char *));
594+
if (cap > INT32_MAX / PAIR_LEN) {
595+
break;
596+
}
597+
int new_cap = cap * PAIR_LEN;
598+
if ((size_t)new_cap > SIZE_MAX / sizeof(*projects)) {
599+
break;
600+
}
601+
char **tmp = realloc(projects, (size_t)new_cap * sizeof(*projects));
598602
if (!tmp) {
599603
break;
600604
}
605+
cap = new_cap;
601606
projects = tmp;
602607
}
603608
/* Strip .db extension */
604609
projects[count] = malloc(len - PAIR_LEN);
610+
if (!projects[count]) {
611+
break;
612+
}
605613
memcpy(projects[count], ent->name, len - CR_DB_EXT_LEN);
606614
projects[count][len - CR_DB_EXT_LEN] = '\0';
607615
count++;

src/pipeline/pass_cross_repo_maven.c

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ bool cbm_cross_repo_maven_grow_array(void **items, int *cap, size_t elem_size,
8686
return false;
8787
}
8888
int new_cap = *cap * PAIR_LEN;
89+
if ((size_t)new_cap > SIZE_MAX / elem_size) {
90+
return false;
91+
}
8992
void *tmp = realloc_fn(*items, (size_t)new_cap * elem_size);
9093
if (!tmp) {
9194
return false;
@@ -370,12 +373,11 @@ static int list_pom_paths(cbm_store_t *store, const char *project, char ***out)
370373
continue;
371374
}
372375
if (count >= cap) {
373-
cap *= PAIR_LEN;
374-
char **tmp = realloc(paths, (size_t)cap * sizeof(char *));
375-
if (!tmp) {
376+
void *grown = paths;
377+
if (!cbm_cross_repo_maven_grow_array(&grown, &cap, sizeof(*paths), realloc)) {
376378
break;
377379
}
378-
paths = tmp;
380+
paths = grown;
379381
}
380382
paths[count] = malloc(strlen(path) + SKIP_ONE);
381383
if (paths[count]) {
@@ -438,16 +440,15 @@ static int collect_artifacts(cbm_store_t *store, const char *project, mcr_artifa
438440
}
439441
if (group && group[0] && artifact && artifact[0]) {
440442
if (count >= cap) {
441-
cap *= PAIR_LEN;
442-
mcr_artifact_t *tmp = realloc(items, (size_t)cap * sizeof(*items));
443-
if (!tmp) {
443+
void *grown = items;
444+
if (!cbm_cross_repo_maven_grow_array(&grown, &cap, sizeof(*items), realloc)) {
444445
free(parent_group);
445446
free(group);
446447
free(artifact);
447448
free(xml);
448449
break;
449450
}
450-
items = tmp;
451+
items = grown;
451452
}
452453
memset(&items[count], 0, sizeof(items[count]));
453454
items[count].group_id = group;

tests/test_pipeline.c

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include <stdlib.h>
1919
#include <string.h>
20+
#include <stdint.h>
2021
#include <stdatomic.h>
2122
#include "foundation/compat_thread.h"
2223
#include <fcntl.h>
@@ -1291,6 +1292,14 @@ static void *test_maven_realloc_fails(void *ptr, size_t size) {
12911292
return NULL;
12921293
}
12931294

1295+
static int g_test_maven_realloc_calls;
1296+
1297+
static void *test_maven_realloc_records_call(void *ptr, size_t size) {
1298+
(void)size;
1299+
g_test_maven_realloc_calls++;
1300+
return ptr;
1301+
}
1302+
12941303
TEST(cross_repo_maven_failed_growth_preserves_capacity) {
12951304
int cap = 32;
12961305
char *items = malloc((size_t)cap);
@@ -1307,6 +1316,24 @@ TEST(cross_repo_maven_failed_growth_preserves_capacity) {
13071316
PASS();
13081317
}
13091318

1319+
TEST(cross_repo_maven_growth_rejects_byte_overflow_before_realloc) {
1320+
int cap = 2;
1321+
char *items = malloc(1);
1322+
ASSERT_NOT_NULL(items);
1323+
void *original = items;
1324+
g_test_maven_realloc_calls = 0;
1325+
1326+
ASSERT_FALSE(cbm_cross_repo_maven_grow_array(
1327+
(void **)&items, &cap, (SIZE_MAX / (size_t)(cap * PAIR_LEN)) + 1,
1328+
test_maven_realloc_records_call));
1329+
ASSERT_EQ(g_test_maven_realloc_calls, 0);
1330+
ASSERT_EQ(cap, 2);
1331+
ASSERT_EQ(items == original, 1);
1332+
1333+
free(items);
1334+
PASS();
1335+
}
1336+
13101337
TEST(cross_repo_maven_dependency_creates_library_edges) {
13111338
const char *provider_pom = "<project><modelVersion>4.0.0</modelVersion>"
13121339
"<groupId>com.example.platform</groupId>"
@@ -1493,7 +1520,16 @@ TEST(cross_repo_maven_cleanup_preserves_unrelated_nodes) {
14931520
.start_line = 1,
14941521
.end_line = 1,
14951522
.properties_json = "{}"};
1523+
cbm_node_t prefixed_function = {.project = "consumer",
1524+
.label = "Function",
1525+
.name = "PrefixedFunction",
1526+
.qualified_name = "__library__manual_function_should_stay",
1527+
.file_path = "src/manual.c",
1528+
.start_line = 1,
1529+
.end_line = 1,
1530+
.properties_json = "{}"};
14961531
ASSERT_GT(cbm_store_upsert_node(consumer, &unrelated), 0);
1532+
ASSERT_GT(cbm_store_upsert_node(consumer, &prefixed_function), 0);
14971533
cbm_store_close(consumer);
14981534

14991535
const char *targets[] = {"provider"};
@@ -1503,12 +1539,49 @@ TEST(cross_repo_maven_cleanup_preserves_unrelated_nodes) {
15031539
consumer = cbm_store_open_path(fx.consumer_db);
15041540
ASSERT_NOT_NULL(consumer);
15051541
ASSERT_TRUE(node_exists_by_qn(consumer, "consumer", "xxlibraryzz_should_stay"));
1542+
ASSERT_TRUE(node_exists_by_qn(consumer, "consumer", "__library__manual_function_should_stay"));
15061543
cbm_store_close(consumer);
15071544

15081545
cleanup_cross_maven_fixture(&fx);
15091546
PASS();
15101547
}
15111548

1549+
TEST(cross_repo_maven_provider_rerun_preserves_incoming_used_by) {
1550+
const char *provider_pom = "<project><modelVersion>4.0.0</modelVersion>"
1551+
"<groupId>com.example.platform</groupId>"
1552+
"<artifactId>shared-library</artifactId>"
1553+
"<version>1.0.0</version></project>";
1554+
const char *consumer_pom =
1555+
"<project><modelVersion>4.0.0</modelVersion>"
1556+
"<groupId>app</groupId><artifactId>consumer</artifactId>"
1557+
"<dependencies><dependency><groupId>com.example.platform</groupId>"
1558+
"<artifactId>shared-library</artifactId><version>1.0.0</version>"
1559+
"</dependency></dependencies></project>";
1560+
cross_maven_fixture_t fx;
1561+
ASSERT_EQ(setup_cross_maven_fixture(&fx, provider_pom, consumer_pom), 0);
1562+
1563+
const char *provider_targets[] = {"provider"};
1564+
cbm_cross_repo_result_t result = cbm_cross_repo_match("consumer", provider_targets, 1);
1565+
ASSERT_EQ(result.library_edges, 1);
1566+
1567+
cbm_store_t *provider = cbm_store_open_path(fx.provider_db);
1568+
ASSERT_NOT_NULL(provider);
1569+
ASSERT_EQ(count_edges_by_type(provider, "provider", "CROSS_LIBRARY_USED_BY"), 1);
1570+
cbm_store_close(provider);
1571+
1572+
const char *consumer_targets[] = {"consumer"};
1573+
result = cbm_cross_repo_match("provider", consumer_targets, 1);
1574+
ASSERT_EQ(result.library_edges, 0);
1575+
1576+
provider = cbm_store_open_path(fx.provider_db);
1577+
ASSERT_NOT_NULL(provider);
1578+
ASSERT_EQ(count_edges_by_type(provider, "provider", "CROSS_LIBRARY_USED_BY"), 1);
1579+
cbm_store_close(provider);
1580+
1581+
cleanup_cross_maven_fixture(&fx);
1582+
PASS();
1583+
}
1584+
15121585
TEST(cross_repo_maven_removed_dependency_clears_provider_used_by) {
15131586
const char *provider_pom = "<project><modelVersion>4.0.0</modelVersion>"
15141587
"<groupId>com.example.platform</groupId>"
@@ -6592,12 +6665,14 @@ SUITE(pipeline) {
65926665
RUN_TEST(pipeline_fastapi_depends_edges);
65936666
/* Cross-repo library dependency linking */
65946667
RUN_TEST(cross_repo_maven_failed_growth_preserves_capacity);
6668+
RUN_TEST(cross_repo_maven_growth_rejects_byte_overflow_before_realloc);
65956669
RUN_TEST(cross_repo_maven_dependency_creates_library_edges);
65966670
RUN_TEST(cross_repo_maven_dependency_escapes_library_edge_props);
65976671
RUN_TEST(cross_repo_maven_dependency_management_does_not_create_library_edge);
65986672
RUN_TEST(cross_repo_maven_commented_dependency_does_not_create_library_edge);
65996673
RUN_TEST(cross_repo_maven_plugin_dependency_does_not_create_library_edge);
66006674
RUN_TEST(cross_repo_maven_cleanup_preserves_unrelated_nodes);
6675+
RUN_TEST(cross_repo_maven_provider_rerun_preserves_incoming_used_by);
66016676
RUN_TEST(cross_repo_maven_removed_dependency_clears_provider_used_by);
66026677
RUN_TEST(cross_repo_maven_long_coordinates_do_not_collide);
66036678
RUN_TEST(cross_repo_maven_fixture_restores_cache_dir);

0 commit comments

Comments
 (0)