From d4cd0b846ec3a870d4d632804d55ec5a411b281a Mon Sep 17 00:00:00 2001 From: soumalya das Date: Sat, 10 Oct 2026 20:04:59 +0530 Subject: [PATCH 01/17] Make dependency expansion cleanup safe on errors --- src/pkg.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/src/pkg.c b/src/pkg.c index fb01878..fc4f951 100644 --- a/src/pkg.c +++ b/src/pkg.c @@ -82,7 +82,8 @@ static PkgDep *pkg_expand_deps(PkgDep *list, int depth, char *error, size_t error_size, - int from); + int from, + bool *ok); /* * Run git and capture its stdout. argv[0] is "git", the rest are plain @@ -2123,6 +2124,7 @@ static int pkg_install_core(bool update_all, const char *update_one) int count = 0; if (!pkg_collect_deps(&manifest, &deps, &count, error, sizeof(error))) { fprintf(stderr, "flint pkg: %s\n", error); + pkg_free_deps(deps, count); pkg_free_manifest(&manifest); return 65; } @@ -2182,6 +2184,7 @@ static int pkg_install_core(bool update_all, const char *update_one) * realloc, and reading the old pointer afterwards is the * use-after-free that produced a garbage path here. */ int filled = count; + bool expand_ok = true; PkgDep *expanded = pkg_expand_deps(deps, count, &capacity, @@ -2190,15 +2193,16 @@ static int pkg_install_core(bool update_all, const char *update_one) 0, error, sizeof(error), - 0); - if (expanded == NULL) { + 0, + &expand_ok); + deps = expanded; + count = filled; + if (!expand_ok) { fprintf(stderr, "flint pkg: %s\n", error); pkg_free_deps(deps, count); pkg_free_locked(locked, locked_count); return 65; } - deps = expanded; - count = filled; /* dedupe by name: two packages can reach the same one by * different routes, and it should be installed once. A name @@ -2956,14 +2960,16 @@ static PkgDep *pkg_expand_deps(PkgDep *list, int depth, char *error, size_t error_size, - int from) + int from, + bool *ok) { if (from == 0 && depth > 32) { snprintf(error, error_size, "dependency chain is deeper than 32 -- is there a " "cycle?"); - return NULL; + *ok = false; + return list; } /* count first: every append can move the array, so the loop below @@ -2993,7 +2999,8 @@ static PkgDep *pkg_expand_deps(PkgDep *list, base_dir, list[i].path); if (!pkg_load(manifest_path, &child, error, error_size)) - return NULL; + *ok = false; + return list; PkgDep *nested = NULL; int nested_count = 0; bool ok = pkg_collect_deps( @@ -3001,7 +3008,8 @@ static PkgDep *pkg_expand_deps(PkgDep *list, pkg_free_manifest(&child); if (!ok) { pkg_free_deps(nested, nested_count); - return NULL; + *ok = false; + return list; } added += nested_count; pkg_free_deps(nested, nested_count); @@ -3020,7 +3028,8 @@ static PkgDep *pkg_expand_deps(PkgDep *list, PkgDep *bigger = realloc(list, (size_t)grown * sizeof(PkgDep)); if (bigger == NULL) { snprintf(error, error_size, "out of memory"); - return NULL; + *ok = false; + return list; } list = bigger; *capacity = grown; @@ -3058,7 +3067,8 @@ static PkgDep *pkg_expand_deps(PkgDep *list, base_dir, list[i].path); if (!pkg_load(manifest_path, &child, error, error_size)) - return NULL; + *ok = false; + return list; PkgDep *nested = NULL; int nested_count = 0; bool ok = pkg_collect_deps( @@ -3066,7 +3076,8 @@ static PkgDep *pkg_expand_deps(PkgDep *list, pkg_free_manifest(&child); if (!ok) { pkg_free_deps(nested, nested_count); - return NULL; + *ok = false; + return list; } for (int k = 0; k < nested_count; k++) { /* a nested path is relative to its parent package */ @@ -3095,7 +3106,9 @@ static PkgDep *pkg_expand_deps(PkgDep *list, nested[k].path = absolute; } list[count++] = nested[k]; + *filled = count; } + free(nested); } *filled = count; From 45d67ce61da8bfb452dcab2dae0ffa9e56505462 Mon Sep 17 00:00:00 2001 From: soumalya das Date: Sat, 10 Oct 2026 20:05:02 +0530 Subject: [PATCH 02/17] Make dependency expansion cleanup safe on errors --- rust/flintlang/c-src/src/pkg.c | 37 +++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/rust/flintlang/c-src/src/pkg.c b/rust/flintlang/c-src/src/pkg.c index aca7b39..4750713 100644 --- a/rust/flintlang/c-src/src/pkg.c +++ b/rust/flintlang/c-src/src/pkg.c @@ -82,7 +82,8 @@ static PkgDep *pkg_expand_deps(PkgDep *list, int depth, char *error, size_t error_size, - int from); + int from, + bool *ok); /* * Run git and capture its stdout. argv[0] is "git", the rest are plain @@ -2093,6 +2094,7 @@ static int pkg_install_core(bool update_all, const char *update_one) int count = 0; if (!pkg_collect_deps(&manifest, &deps, &count, error, sizeof(error))) { fprintf(stderr, "flint pkg: %s\n", error); + pkg_free_deps(deps, count); pkg_free_manifest(&manifest); return 65; } @@ -2152,6 +2154,7 @@ static int pkg_install_core(bool update_all, const char *update_one) * realloc, and reading the old pointer afterwards is the * use-after-free that produced a garbage path here. */ int filled = count; + bool expand_ok = true; PkgDep *expanded = pkg_expand_deps(deps, count, &capacity, @@ -2160,15 +2163,16 @@ static int pkg_install_core(bool update_all, const char *update_one) 0, error, sizeof(error), - 0); - if (expanded == NULL) { + 0, + &expand_ok); + deps = expanded; + count = filled; + if (!expand_ok) { fprintf(stderr, "flint pkg: %s\n", error); pkg_free_deps(deps, count); pkg_free_locked(locked, locked_count); return 65; } - deps = expanded; - count = filled; /* dedupe by name: two packages can reach the same one by * different routes, and it should be installed once. A name @@ -2926,14 +2930,16 @@ static PkgDep *pkg_expand_deps(PkgDep *list, int depth, char *error, size_t error_size, - int from) + int from, + bool *ok) { if (from == 0 && depth > 32) { snprintf(error, error_size, "dependency chain is deeper than 32 -- is there a " "cycle?"); - return NULL; + *ok = false; + return list; } /* count first: every append can move the array, so the loop below @@ -2963,7 +2969,8 @@ static PkgDep *pkg_expand_deps(PkgDep *list, base_dir, list[i].path); if (!pkg_load(manifest_path, &child, error, error_size)) - return NULL; + *ok = false; + return list; PkgDep *nested = NULL; int nested_count = 0; bool ok = pkg_collect_deps( @@ -2971,7 +2978,8 @@ static PkgDep *pkg_expand_deps(PkgDep *list, pkg_free_manifest(&child); if (!ok) { pkg_free_deps(nested, nested_count); - return NULL; + *ok = false; + return list; } added += nested_count; pkg_free_deps(nested, nested_count); @@ -2990,7 +2998,8 @@ static PkgDep *pkg_expand_deps(PkgDep *list, PkgDep *bigger = realloc(list, (size_t)grown * sizeof(PkgDep)); if (bigger == NULL) { snprintf(error, error_size, "out of memory"); - return NULL; + *ok = false; + return list; } list = bigger; *capacity = grown; @@ -3028,7 +3037,8 @@ static PkgDep *pkg_expand_deps(PkgDep *list, base_dir, list[i].path); if (!pkg_load(manifest_path, &child, error, error_size)) - return NULL; + *ok = false; + return list; PkgDep *nested = NULL; int nested_count = 0; bool ok = pkg_collect_deps( @@ -3036,7 +3046,8 @@ static PkgDep *pkg_expand_deps(PkgDep *list, pkg_free_manifest(&child); if (!ok) { pkg_free_deps(nested, nested_count); - return NULL; + *ok = false; + return list; } for (int k = 0; k < nested_count; k++) { /* a nested path is relative to its parent package */ @@ -3065,7 +3076,9 @@ static PkgDep *pkg_expand_deps(PkgDep *list, nested[k].path = absolute; } list[count++] = nested[k]; + *filled = count; } + free(nested); } *filled = count; From 724c06d8eefe06c7c101e6d54185ceff78526a5a Mon Sep 17 00:00:00 2001 From: soumalya das Date: Sat, 10 Oct 2026 20:05:21 +0530 Subject: [PATCH 03/17] Correct error propagation in dependency expansion --- src/pkg.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/pkg.c b/src/pkg.c index fc4f951..f22d859 100644 --- a/src/pkg.c +++ b/src/pkg.c @@ -83,7 +83,7 @@ static PkgDep *pkg_expand_deps(PkgDep *list, char *error, size_t error_size, int from, - bool *ok); + bool *success); /* * Run git and capture its stdout. argv[0] is "git", the rest are plain @@ -2961,14 +2961,14 @@ static PkgDep *pkg_expand_deps(PkgDep *list, char *error, size_t error_size, int from, - bool *ok) + bool *success) { if (from == 0 && depth > 32) { snprintf(error, error_size, "dependency chain is deeper than 32 -- is there a " "cycle?"); - *ok = false; + *success = false; return list; } @@ -2998,9 +2998,10 @@ static PkgDep *pkg_expand_deps(PkgDep *list, "%s/%s/flint.toml", base_dir, list[i].path); - if (!pkg_load(manifest_path, &child, error, error_size)) - *ok = false; + if (!pkg_load(manifest_path, &child, error, error_size)) { + *success = false; return list; + } PkgDep *nested = NULL; int nested_count = 0; bool ok = pkg_collect_deps( @@ -3008,7 +3009,7 @@ static PkgDep *pkg_expand_deps(PkgDep *list, pkg_free_manifest(&child); if (!ok) { pkg_free_deps(nested, nested_count); - *ok = false; + *success = false; return list; } added += nested_count; @@ -3028,7 +3029,7 @@ static PkgDep *pkg_expand_deps(PkgDep *list, PkgDep *bigger = realloc(list, (size_t)grown * sizeof(PkgDep)); if (bigger == NULL) { snprintf(error, error_size, "out of memory"); - *ok = false; + *success = false; return list; } list = bigger; @@ -3066,9 +3067,10 @@ static PkgDep *pkg_expand_deps(PkgDep *list, "%s/%s/flint.toml", base_dir, list[i].path); - if (!pkg_load(manifest_path, &child, error, error_size)) - *ok = false; + if (!pkg_load(manifest_path, &child, error, error_size)) { + *success = false; return list; + } PkgDep *nested = NULL; int nested_count = 0; bool ok = pkg_collect_deps( @@ -3076,7 +3078,7 @@ static PkgDep *pkg_expand_deps(PkgDep *list, pkg_free_manifest(&child); if (!ok) { pkg_free_deps(nested, nested_count); - *ok = false; + *success = false; return list; } for (int k = 0; k < nested_count; k++) { @@ -3121,5 +3123,6 @@ static PkgDep *pkg_expand_deps(PkgDep *list, depth + 1, error, error_size, - start); + start, + success); } From 32808bcc6361353f375ad67cae9c6a320cc66bc5 Mon Sep 17 00:00:00 2001 From: soumalya das Date: Sat, 10 Oct 2026 20:05:24 +0530 Subject: [PATCH 04/17] Correct error propagation in dependency expansion --- rust/flintlang/c-src/src/pkg.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/rust/flintlang/c-src/src/pkg.c b/rust/flintlang/c-src/src/pkg.c index 4750713..99f262e 100644 --- a/rust/flintlang/c-src/src/pkg.c +++ b/rust/flintlang/c-src/src/pkg.c @@ -83,7 +83,7 @@ static PkgDep *pkg_expand_deps(PkgDep *list, char *error, size_t error_size, int from, - bool *ok); + bool *success); /* * Run git and capture its stdout. argv[0] is "git", the rest are plain @@ -2931,14 +2931,14 @@ static PkgDep *pkg_expand_deps(PkgDep *list, char *error, size_t error_size, int from, - bool *ok) + bool *success) { if (from == 0 && depth > 32) { snprintf(error, error_size, "dependency chain is deeper than 32 -- is there a " "cycle?"); - *ok = false; + *success = false; return list; } @@ -2968,9 +2968,10 @@ static PkgDep *pkg_expand_deps(PkgDep *list, "%s/%s/flint.toml", base_dir, list[i].path); - if (!pkg_load(manifest_path, &child, error, error_size)) - *ok = false; + if (!pkg_load(manifest_path, &child, error, error_size)) { + *success = false; return list; + } PkgDep *nested = NULL; int nested_count = 0; bool ok = pkg_collect_deps( @@ -2978,7 +2979,7 @@ static PkgDep *pkg_expand_deps(PkgDep *list, pkg_free_manifest(&child); if (!ok) { pkg_free_deps(nested, nested_count); - *ok = false; + *success = false; return list; } added += nested_count; @@ -2998,7 +2999,7 @@ static PkgDep *pkg_expand_deps(PkgDep *list, PkgDep *bigger = realloc(list, (size_t)grown * sizeof(PkgDep)); if (bigger == NULL) { snprintf(error, error_size, "out of memory"); - *ok = false; + *success = false; return list; } list = bigger; @@ -3036,9 +3037,10 @@ static PkgDep *pkg_expand_deps(PkgDep *list, "%s/%s/flint.toml", base_dir, list[i].path); - if (!pkg_load(manifest_path, &child, error, error_size)) - *ok = false; + if (!pkg_load(manifest_path, &child, error, error_size)) { + *success = false; return list; + } PkgDep *nested = NULL; int nested_count = 0; bool ok = pkg_collect_deps( @@ -3046,7 +3048,7 @@ static PkgDep *pkg_expand_deps(PkgDep *list, pkg_free_manifest(&child); if (!ok) { pkg_free_deps(nested, nested_count); - *ok = false; + *success = false; return list; } for (int k = 0; k < nested_count; k++) { @@ -3091,5 +3093,6 @@ static PkgDep *pkg_expand_deps(PkgDep *list, depth + 1, error, error_size, - start); + start, + success); } From ca185baf3b2a067fd47f56e236c518e555dff044 Mon Sep 17 00:00:00 2001 From: soumalya das Date: Sat, 10 Oct 2026 20:05:47 +0530 Subject: [PATCH 05/17] Format dependency expansion error propagation --- src/pkg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pkg.c b/src/pkg.c index f22d859..4d4b738 100644 --- a/src/pkg.c +++ b/src/pkg.c @@ -3124,5 +3124,5 @@ static PkgDep *pkg_expand_deps(PkgDep *list, error, error_size, start, - success); + success); } From 0737b54fff299299e7a284c3f99f3909a4cdd8f7 Mon Sep 17 00:00:00 2001 From: soumalya das Date: Sat, 10 Oct 2026 20:05:51 +0530 Subject: [PATCH 06/17] Format dependency expansion error propagation --- rust/flintlang/c-src/src/pkg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/flintlang/c-src/src/pkg.c b/rust/flintlang/c-src/src/pkg.c index 99f262e..6c10abc 100644 --- a/rust/flintlang/c-src/src/pkg.c +++ b/rust/flintlang/c-src/src/pkg.c @@ -3094,5 +3094,5 @@ static PkgDep *pkg_expand_deps(PkgDep *list, error, error_size, start, - success); + success); } From 4ab08f39f6c8c1befc985ac17049ffe60816f531 Mon Sep 17 00:00:00 2001 From: soumalya das Date: Sat, 10 Oct 2026 20:05:59 +0530 Subject: [PATCH 07/17] Add transitive dependency test fixture --- tests/pkg/base/flint.toml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tests/pkg/base/flint.toml diff --git a/tests/pkg/base/flint.toml b/tests/pkg/base/flint.toml new file mode 100644 index 0000000..a613963 --- /dev/null +++ b/tests/pkg/base/flint.toml @@ -0,0 +1,3 @@ +[package] +name = "base" +version = "0.1.0" From 7ec5cf174e123fac1fb668a5826ba75ff6bb7c4b Mon Sep 17 00:00:00 2001 From: soumalya das Date: Sat, 10 Oct 2026 20:12:02 +0530 Subject: [PATCH 08/17] Handle allocation failure during dependency path normalization --- src/pkg.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/pkg.c b/src/pkg.c index 4d4b738..4d74077 100644 --- a/src/pkg.c +++ b/src/pkg.c @@ -3105,6 +3105,14 @@ static PkgDep *pkg_expand_deps(PkgDep *list, char *absolute = pkg_normalize_path( joined, nested[k].path); free(nested[k].path); + if (absolute == NULL) { + nested[k].path = NULL; + pkg_free_deps(nested, nested_count); + free(nested); + snprintf(error, error_size, "out of memory"); + *success = false; + return list; + } nested[k].path = absolute; } list[count++] = nested[k]; From 7d13872b4416aad9aeb1ab97a5953e4f6367f169 Mon Sep 17 00:00:00 2001 From: soumalya das Date: Sat, 10 Oct 2026 20:12:05 +0530 Subject: [PATCH 09/17] Handle allocation failure during dependency path normalization --- rust/flintlang/c-src/src/pkg.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rust/flintlang/c-src/src/pkg.c b/rust/flintlang/c-src/src/pkg.c index 6c10abc..17dbcee 100644 --- a/rust/flintlang/c-src/src/pkg.c +++ b/rust/flintlang/c-src/src/pkg.c @@ -3075,6 +3075,14 @@ static PkgDep *pkg_expand_deps(PkgDep *list, char *absolute = pkg_normalize_path( joined, nested[k].path); free(nested[k].path); + if (absolute == NULL) { + nested[k].path = NULL; + pkg_free_deps(nested, nested_count); + free(nested); + snprintf(error, error_size, "out of memory"); + *success = false; + return list; + } nested[k].path = absolute; } list[count++] = nested[k]; From d04357c0d1acd742804d3dbd881a4d56c83c3463 Mon Sep 17 00:00:00 2001 From: soumalya das Date: Sat, 10 Oct 2026 20:12:18 +0530 Subject: [PATCH 10/17] Keep transferred dependencies out of error cleanup --- src/pkg.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pkg.c b/src/pkg.c index 4d74077..8887bc2 100644 --- a/src/pkg.c +++ b/src/pkg.c @@ -3116,6 +3116,7 @@ static PkgDep *pkg_expand_deps(PkgDep *list, nested[k].path = absolute; } list[count++] = nested[k]; + memset(&nested[k], 0, sizeof(nested[k])); *filled = count; } free(nested); From 47df0ecec804b403df6ed747a00b9e589a3273a8 Mon Sep 17 00:00:00 2001 From: soumalya das Date: Sat, 10 Oct 2026 20:12:22 +0530 Subject: [PATCH 11/17] Keep transferred dependencies out of error cleanup --- rust/flintlang/c-src/src/pkg.c | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/flintlang/c-src/src/pkg.c b/rust/flintlang/c-src/src/pkg.c index 17dbcee..4afcdcd 100644 --- a/rust/flintlang/c-src/src/pkg.c +++ b/rust/flintlang/c-src/src/pkg.c @@ -3086,6 +3086,7 @@ static PkgDep *pkg_expand_deps(PkgDep *list, nested[k].path = absolute; } list[count++] = nested[k]; + memset(&nested[k], 0, sizeof(nested[k])); *filled = count; } free(nested); From 82097d130e0562102110130b1467e26461f3031f Mon Sep 17 00:00:00 2001 From: soumalya das Date: Sat, 10 Oct 2026 20:12:47 +0530 Subject: [PATCH 12/17] Bound dependency expansion and path errors --- src/pkg.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/pkg.c b/src/pkg.c index 8887bc2..7dda277 100644 --- a/src/pkg.c +++ b/src/pkg.c @@ -2963,7 +2963,7 @@ static PkgDep *pkg_expand_deps(PkgDep *list, int from, bool *success) { - if (from == 0 && depth > 32) { + if (depth > 32) { snprintf(error, error_size, "dependency chain is deeper than 32 -- is there a " @@ -2987,17 +2987,23 @@ static PkgDep *pkg_expand_deps(PkgDep *list, * reads as though it does not, and the lockfile should not * carry that spelling either. */ + int path_len; if (strcmp(base_dir, ".") == 0) - snprintf(manifest_path, + path_len = snprintf(manifest_path, sizeof(manifest_path), "%s/flint.toml", list[i].path); else - snprintf(manifest_path, + path_len = snprintf(manifest_path, sizeof(manifest_path), "%s/%s/flint.toml", base_dir, list[i].path); + if (path_len < 0 || (size_t)path_len >= sizeof(manifest_path)) { + snprintf(error, error_size, "dependency manifest path too long"); + *success = false; + return list; + } if (!pkg_load(manifest_path, &child, error, error_size)) { *success = false; return list; @@ -3056,17 +3062,23 @@ static PkgDep *pkg_expand_deps(PkgDep *list, * reads as though it does not, and the lockfile should not * carry that spelling either. */ + int path_len; if (strcmp(base_dir, ".") == 0) - snprintf(manifest_path, + path_len = snprintf(manifest_path, sizeof(manifest_path), "%s/flint.toml", list[i].path); else - snprintf(manifest_path, + path_len = snprintf(manifest_path, sizeof(manifest_path), "%s/%s/flint.toml", base_dir, list[i].path); + if (path_len < 0 || (size_t)path_len >= sizeof(manifest_path)) { + snprintf(error, error_size, "dependency manifest path too long"); + *success = false; + return list; + } if (!pkg_load(manifest_path, &child, error, error_size)) { *success = false; return list; From 8966c0b4f58f8b418a622173aea6dc4fe34a2a14 Mon Sep 17 00:00:00 2001 From: soumalya das Date: Sat, 10 Oct 2026 20:12:50 +0530 Subject: [PATCH 13/17] Bound dependency expansion and path errors --- rust/flintlang/c-src/src/pkg.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/rust/flintlang/c-src/src/pkg.c b/rust/flintlang/c-src/src/pkg.c index 4afcdcd..171a07f 100644 --- a/rust/flintlang/c-src/src/pkg.c +++ b/rust/flintlang/c-src/src/pkg.c @@ -2933,7 +2933,7 @@ static PkgDep *pkg_expand_deps(PkgDep *list, int from, bool *success) { - if (from == 0 && depth > 32) { + if (depth > 32) { snprintf(error, error_size, "dependency chain is deeper than 32 -- is there a " @@ -2957,17 +2957,23 @@ static PkgDep *pkg_expand_deps(PkgDep *list, * reads as though it does not, and the lockfile should not * carry that spelling either. */ + int path_len; if (strcmp(base_dir, ".") == 0) - snprintf(manifest_path, + path_len = snprintf(manifest_path, sizeof(manifest_path), "%s/flint.toml", list[i].path); else - snprintf(manifest_path, + path_len = snprintf(manifest_path, sizeof(manifest_path), "%s/%s/flint.toml", base_dir, list[i].path); + if (path_len < 0 || (size_t)path_len >= sizeof(manifest_path)) { + snprintf(error, error_size, "dependency manifest path too long"); + *success = false; + return list; + } if (!pkg_load(manifest_path, &child, error, error_size)) { *success = false; return list; @@ -3026,17 +3032,23 @@ static PkgDep *pkg_expand_deps(PkgDep *list, * reads as though it does not, and the lockfile should not * carry that spelling either. */ + int path_len; if (strcmp(base_dir, ".") == 0) - snprintf(manifest_path, + path_len = snprintf(manifest_path, sizeof(manifest_path), "%s/flint.toml", list[i].path); else - snprintf(manifest_path, + path_len = snprintf(manifest_path, sizeof(manifest_path), "%s/%s/flint.toml", base_dir, list[i].path); + if (path_len < 0 || (size_t)path_len >= sizeof(manifest_path)) { + snprintf(error, error_size, "dependency manifest path too long"); + *success = false; + return list; + } if (!pkg_load(manifest_path, &child, error, error_size)) { *success = false; return list; From 2f1994482f78a4fca5b9fc5b6fbb51adc7cb5ce5 Mon Sep 17 00:00:00 2001 From: soumalya das Date: Sat, 10 Oct 2026 20:13:03 +0530 Subject: [PATCH 14/17] Test cyclic dependency rejection --- tests/pkg_test.sh | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/pkg_test.sh b/tests/pkg_test.sh index 7d1d2d6..dd290a9 100644 --- a/tests/pkg_test.sh +++ b/tests/pkg_test.sh @@ -297,6 +297,37 @@ expect_contains "the lock records the nested source" "../base" \ "$FLINT" pkg install >/dev/null 2>&1 expect "reinstalling the chain succeeds" 0 "$?" +# Cyclic path dependencies must stop with an error, not recurse indefinitely. +mkdir -p "$WORK/cycle/a" "$WORK/cycle/b" "$WORK/cycle/app" +cat > "$WORK/cycle/a/flint.toml" <<'EOF3' +[package] +name = "cycle-a" +version = "1.0.0" + +[dependencies] +cycle-b = { path = "../b" } +EOF3 +cat > "$WORK/cycle/b/flint.toml" <<'EOF3' +[package] +name = "cycle-b" +version = "1.0.0" + +[dependencies] +cycle-a = { path = "../a" } +EOF3 +cat > "$WORK/cycle/app/flint.toml" <<'EOF3' +[package] +name = "cycle-app" +version = "0.1.0" + +[dependencies] +cycle-a = { path = "../a" } +EOF3 +cd "$WORK/cycle/app" || exit 1 +out=$("$FLINT" pkg install 2>&1) && status=0 || status=$? +expect "cyclic dependencies fail" "1" "$([ "$status" -ne 0 ] && echo 1 || echo 0)" +expect_contains "cycle failure is bounded and clear" "dependency chain is deeper than 32" "$out" + cd "$WORK" || exit 1 if [ "$failed" -ne 0 ]; then echo "pkg tests: $checks checks, $failed failures" From 856618421a504fe386bfec262af603fd5861177a Mon Sep 17 00:00:00 2001 From: soumalya das Date: Sat, 10 Oct 2026 20:14:37 +0530 Subject: [PATCH 15/17] Reject overlong transitive dependency paths --- src/pkg.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/pkg.c b/src/pkg.c index 7dda277..a0a80c5 100644 --- a/src/pkg.c +++ b/src/pkg.c @@ -3103,17 +3103,28 @@ static PkgDep *pkg_expand_deps(PkgDep *list, * it, and "." must not contribute a * component or the lockfile carries a * spelling nobody wrote. */ + int joined_len; if (strcmp(base_dir, ".") == 0) - snprintf(joined, + joined_len = snprintf(joined, sizeof(joined), "%s", list[i].path); else - snprintf(joined, + joined_len = snprintf(joined, sizeof(joined), "%s/%s", base_dir, list[i].path); + if (joined_len < 0 || + (size_t)joined_len >= sizeof(joined)) { + pkg_free_deps(nested, nested_count); + free(nested); + snprintf(error, + error_size, + "dependency path is too long"); + *success = false; + return list; + } char *absolute = pkg_normalize_path( joined, nested[k].path); free(nested[k].path); From ec15a88d817a6799b5a917f56c0ed4982dbfc82e Mon Sep 17 00:00:00 2001 From: soumalya das Date: Sat, 10 Oct 2026 20:14:42 +0530 Subject: [PATCH 16/17] Reject overlong transitive dependency paths --- rust/flintlang/c-src/src/pkg.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/rust/flintlang/c-src/src/pkg.c b/rust/flintlang/c-src/src/pkg.c index 171a07f..1d3564c 100644 --- a/rust/flintlang/c-src/src/pkg.c +++ b/rust/flintlang/c-src/src/pkg.c @@ -3073,17 +3073,28 @@ static PkgDep *pkg_expand_deps(PkgDep *list, * it, and "." must not contribute a * component or the lockfile carries a * spelling nobody wrote. */ + int joined_len; if (strcmp(base_dir, ".") == 0) - snprintf(joined, + joined_len = snprintf(joined, sizeof(joined), "%s", list[i].path); else - snprintf(joined, + joined_len = snprintf(joined, sizeof(joined), "%s/%s", base_dir, list[i].path); + if (joined_len < 0 || + (size_t)joined_len >= sizeof(joined)) { + pkg_free_deps(nested, nested_count); + free(nested); + snprintf(error, + error_size, + "dependency path is too long"); + *success = false; + return list; + } char *absolute = pkg_normalize_path( joined, nested[k].path); free(nested[k].path); From 1d4e2bf64179814996222d3fad28afc7ebe53db3 Mon Sep 17 00:00:00 2001 From: soumalya das Date: Sat, 10 Oct 2026 20:21:33 +0530 Subject: [PATCH 17/17] Remove unused dependency fixture --- tests/pkg/base/flint.toml | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 tests/pkg/base/flint.toml diff --git a/tests/pkg/base/flint.toml b/tests/pkg/base/flint.toml deleted file mode 100644 index a613963..0000000 --- a/tests/pkg/base/flint.toml +++ /dev/null @@ -1,3 +0,0 @@ -[package] -name = "base" -version = "0.1.0"