Skip to content

Commit 0823077

Browse files
committed
fix lib OOM related leaks / issue & ENSURE_ARRAY_CAP + defensive raw *ptr
1 parent eac1eb1 commit 0823077

File tree

4 files changed

+62
-25
lines changed

4 files changed

+62
-25
lines changed

.github/test.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12070,6 +12070,22 @@ void test_typeof_volatile_inner_zeroed(void)
1207012070
}
1207112071
#endif
1207212072

12073+
typedef int *IntPtr;
12074+
void test_raw_star_ptr_decl(void)
12075+
{
12076+
int x = 42;
12077+
{
12078+
defer (void)0;
12079+
raw IntPtr p;
12080+
p = &x;
12081+
CHECK_EQ(*p, 42, "raw typedef ptr declaration");
12082+
12083+
raw int *q;
12084+
q = &x;
12085+
CHECK_EQ(*q, 42, "raw int *q declaration");
12086+
}
12087+
}
12088+
1207312089
void run_issue_validation_tests(void)
1207412090
{
1207512091
printf("\n=== ISSUE VALIDATION TESTS ===\n");
@@ -12116,6 +12132,7 @@ void run_issue_validation_tests(void)
1211612132
test_label_zeroinit_in_stmt_expr();
1211712133
test_typeof_volatile_inner_zeroed();
1211812134
#endif
12135+
test_raw_star_ptr_decl();
1211912136
}
1212012137

1212112138
int main(void)

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
Prism is a lightweight and very fast transpiler that makes C safer without changing how you write it.
77

8-
- **1274 tests** — edge cases, control flow, nightmares, trying hard to break Prism
8+
- **1276 tests** — edge cases, control flow, nightmares, trying hard to break Prism
99
- **Building Real C** — OpenSSL, SQLite, Bash, GNU Coreutils, Make, Curl
1010
- **Proper transpiler** — tracks typedefs, respects scope, catches unsafe patterns
1111
- **Opt-out features** Disable parts of the transpiler, like zero-init, with CLI flags

parse.c

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,15 +78,16 @@ static const uint8_t ident_char[256] = {
7878
{ \
7979
if ((count) >= (cap)) \
8080
{ \
81-
int new_cap = (cap) == 0 ? (init_cap) : (cap) * 2; \
82-
while (new_cap < (count)) \
81+
size_t new_cap = (cap) == 0 ? (init_cap) : (size_t)(cap) * 2; \
82+
while (new_cap < (size_t)(count)) \
8383
new_cap *= 2; \
8484
T *old_ptr_ = (arr); \
8585
T *tmp = realloc((arr), sizeof(T) * new_cap); \
8686
if (!tmp) \
8787
{ \
8888
free(old_ptr_); \
8989
(arr) = NULL; \
90+
(cap) = 0; \
9091
error("out of memory"); \
9192
} \
9293
(arr) = tmp; \
@@ -1305,8 +1306,17 @@ static File *new_file(char *name, int file_no, char *contents)
13051306
{
13061307
File *file = calloc(1, sizeof(File));
13071308
if (!file)
1309+
{
1310+
free(contents);
13081311
error("out of memory");
1312+
}
13091313
file->name = strdup(name);
1314+
if (!file->name)
1315+
{
1316+
free(contents);
1317+
free(file);
1318+
error("out of memory");
1319+
}
13101320
file->display_name = file->name;
13111321
file->file_no = file_no;
13121322
file->contents = contents;
@@ -1660,12 +1670,16 @@ static Token *tokenize(File *file)
16601670
// to avoid writing/reading temp files.
16611671
static Token *tokenize_buffer(char *name, char *buf)
16621672
{
1663-
if (!ctx->keyword_map.capacity)
1664-
init_keyword_map();
1665-
16661673
if (!buf)
16671674
return NULL;
16681675

1676+
// Init keyword map before new_file() takes ownership of buf.
1677+
// If init_keyword_map() fails (OOM → longjmp in lib mode), buf hasn't
1678+
// been stored yet, so the caller can still free it. By failing early
1679+
// here, we avoid leaking buf.
1680+
if (!ctx->keyword_map.capacity)
1681+
init_keyword_map();
1682+
16691683
File *file = new_file(name, ctx->input_file_count, buf);
16701684
add_input_file(file);
16711685

@@ -1717,9 +1731,12 @@ void tokenizer_teardown(bool full)
17171731
else
17181732
arena_reset(&ctx->main_arena);
17191733
free_file_view_cache();
1720-
for (int i = 0; i < ctx->input_file_count; i++)
1721-
free_file(ctx->input_files[i]);
1722-
free(ctx->input_files);
1734+
if (ctx->input_files)
1735+
{
1736+
for (int i = 0; i < ctx->input_file_count; i++)
1737+
free_file(ctx->input_files[i]);
1738+
free(ctx->input_files);
1739+
}
17231740
ctx->input_files = NULL;
17241741
ctx->input_file_count = 0;
17251742
ctx->input_file_capacity = 0;

prism.c

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -503,9 +503,12 @@ static void emit_system_includes(void)
503503
static void system_includes_reset(void)
504504
{
505505
hashmap_clear(&system_includes);
506-
for (int i = 0; i < ctx->system_include_count; i++)
507-
free(system_include_list[i]);
508-
free(system_include_list);
506+
if (system_include_list)
507+
{
508+
for (int i = 0; i < ctx->system_include_count; i++)
509+
free(system_include_list[i]);
510+
free(system_include_list);
511+
}
509512
system_include_list = NULL;
510513
ctx->system_include_count = 0;
511514
system_include_capacity = 0;
@@ -2133,6 +2136,7 @@ static bool is_var_declaration(Token *type_end)
21332136
static bool is_raw_declaration_context(Token *after_raw)
21342137
{
21352138
return after_raw && (is_type_keyword(after_raw) || is_known_typedef(after_raw) ||
2139+
equal(after_raw, "*") ||
21362140
(after_raw->tag & (TT_QUALIFIER | TT_SUE)));
21372141
}
21382142

@@ -2782,7 +2786,10 @@ typedef struct
27822786
static inline void argv_builder_add(ArgvBuilder *ab, const char *arg)
27832787
{
27842788
ENSURE_ARRAY_CAP(ab->data, ab->count + 2, ab->capacity, 64, char *);
2785-
ab->data[ab->count++] = strdup(arg);
2789+
ab->data[ab->count] = strdup(arg);
2790+
if (!ab->data[ab->count])
2791+
error("out of memory");
2792+
ab->count++;
27862793
ab->data[ab->count] = NULL;
27872794
}
27882795
#define argv_builder_finish(ab) ((ab)->data)
@@ -2940,21 +2947,12 @@ static int make_temp_file(char *buf, size_t bufsize, const char *prefix, int suf
29402947
return -1;
29412948
close(fd);
29422949
#else
2943-
int fd = mkstemp(buf);
2950+
// Fallback: use mkstemps where available, otherwise mkstemp.
2951+
// mkstemps is widely available on any system that has mkstemp.
2952+
int fd = suffix_len > 0 ? mkstemps(buf, suffix_len) : mkstemp(buf);
29442953
if (fd < 0)
29452954
return -1;
29462955
close(fd);
2947-
if (suffix_len > 0)
2948-
{
2949-
unlink(buf);
2950-
size_t len = strlen(buf);
2951-
if (len + suffix_len < bufsize)
2952-
{
2953-
buf[len] = '.';
2954-
buf[len + 1] = 'c';
2955-
buf[len + 2] = '\0';
2956-
}
2957-
}
29582956
#endif
29592957
return 0;
29602958
}
@@ -3143,6 +3141,11 @@ static int transpile_tokens(Token *tok, FILE *fp);
31433141
// This is the original interface used by the CLI and library API.
31443142
static int transpile(char *input_file, char *output_file)
31453143
{
3144+
// Ensure keyword map is initialized before allocating preprocessor buffer.
3145+
// If init fails (OOM → longjmp in lib mode), we haven't allocated pp_buf yet.
3146+
if (!ctx->keyword_map.capacity)
3147+
init_keyword_map();
3148+
31463149
// Run system preprocessor via pipe — no temp files
31473150
char *pp_buf = preprocess_with_cc(input_file);
31483151
if (!pp_buf)

0 commit comments

Comments
 (0)