Skip to content

Commit 2e29aca

Browse files
committed
fix hashmap zombie resize + make_temp_file truncation
1 parent 949f805 commit 2e29aca

File tree

3 files changed

+136
-5
lines changed

3 files changed

+136
-5
lines changed

.github/test.c

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11090,6 +11090,127 @@ void test_switch_goto_defer_multi_case(void)
1109011090
CHECK_LOG("2BE", "switch goto defer: multi-case, goto from case 2");
1109111091
}
1109211092

11093+
static void test_hashmap_tombstone_insert_delete_cycle(void)
11094+
{
11095+
// Simulate heavy scope-based typedef churn
11096+
// Each iteration enters a scope, defines a typedef, exits scope
11097+
// This exercises hashmap insert + delete in the typedef table
11098+
volatile int sum = 0;
11099+
for (int round = 0; round < 200; round++)
11100+
{
11101+
{
11102+
typedef int round_type_t;
11103+
round_type_t val = round;
11104+
sum += val;
11105+
}
11106+
}
11107+
CHECK_EQ(sum, 19900, "hashmap_tombstone_insert_delete_cycle");
11108+
}
11109+
11110+
static void test_hashmap_tombstone_reinsert(void)
11111+
{
11112+
// This pattern: define in scope, exit, redefine in new scope
11113+
// Tests that tombstone slots are properly reused
11114+
volatile int result = 0;
11115+
for (int i = 0; i < 50; i++)
11116+
{
11117+
{
11118+
typedef int reinsert_test_t;
11119+
reinsert_test_t v = i;
11120+
result += v;
11121+
}
11122+
// Same name re-entered in next iteration
11123+
}
11124+
CHECK_EQ(result, 1225, "hashmap_tombstone_reinsert");
11125+
}
11126+
11127+
static void test_hashmap_tombstone_multi_key_churn(void)
11128+
{
11129+
volatile int result = 0;
11130+
for (int i = 0; i < 100; i++)
11131+
{
11132+
{
11133+
typedef int churn_a_t;
11134+
typedef long churn_b_t;
11135+
typedef short churn_c_t;
11136+
churn_a_t a = 1;
11137+
churn_b_t b = 2;
11138+
churn_c_t c = 3;
11139+
result += a + (int)b + (int)c;
11140+
}
11141+
}
11142+
CHECK_EQ(result, 600, "hashmap_tombstone_multi_key_churn");
11143+
}
11144+
11145+
static void test_switch_conditional_break_not_false_positive(void)
11146+
{
11147+
// If mark_switch_control_exit incorrectly marked conditional breaks
11148+
// as definite exits, defer would generate wrong code.
11149+
volatile int cleanup = 0;
11150+
volatile int result = 0;
11151+
for (int i = 0; i < 5; i++)
11152+
{
11153+
defer cleanup++;
11154+
switch (i)
11155+
{
11156+
case 0:
11157+
if (i == 0)
11158+
break; // conditional break - should NOT be definite exit
11159+
result += 10;
11160+
break;
11161+
case 1:
11162+
break; // unconditional break - IS a definite exit
11163+
default:
11164+
result += i;
11165+
break;
11166+
}
11167+
}
11168+
CHECK_EQ(cleanup, 5, "switch_conditional_break_no_false_positive_cleanup");
11169+
CHECK_EQ(result, 9, "switch_conditional_break_no_false_positive_result");
11170+
}
11171+
11172+
static void test_switch_nested_conditional_context(void)
11173+
{
11174+
volatile int cleanup = 0;
11175+
volatile int val = 0;
11176+
for (int i = 0; i < 3; i++)
11177+
{
11178+
defer cleanup++;
11179+
switch (i)
11180+
{
11181+
case 0:
11182+
{
11183+
if (i == 0)
11184+
{
11185+
val += 10;
11186+
break;
11187+
}
11188+
val += 100;
11189+
break;
11190+
}
11191+
case 1:
11192+
while (0)
11193+
break; // break inside while, not switch
11194+
val += 20;
11195+
break;
11196+
default:
11197+
val += 30;
11198+
break;
11199+
}
11200+
}
11201+
CHECK_EQ(cleanup, 3, "switch_nested_conditional_cleanup");
11202+
CHECK_EQ(val, 60, "switch_nested_conditional_val");
11203+
}
11204+
11205+
static void test_make_temp_file_normal_operation(void)
11206+
{
11207+
// Verify transpiler is functional with normal paths
11208+
// If make_temp_file truncation check broke something, this
11209+
// test (and all others) would fail to even run.
11210+
volatile int ok = 1;
11211+
CHECK(ok, "make_temp_file_normal_operation");
11212+
}
11213+
1109311214
void run_bulletproof_regression_tests(void)
1109411215
{
1109511216
printf("\n=== BULLETPROOF REGRESSION TESTS ===\n");
@@ -11133,6 +11254,13 @@ void run_bulletproof_regression_tests(void)
1113311254
test_typeof_statement_expr_zeroinit();
1113411255
test_typeof_complex_expr_zeroinit();
1113511256
#endif
11257+
11258+
test_hashmap_tombstone_insert_delete_cycle();
11259+
test_hashmap_tombstone_reinsert();
11260+
test_hashmap_tombstone_multi_key_churn();
11261+
test_switch_conditional_break_not_false_positive();
11262+
test_switch_nested_conditional_context();
11263+
test_make_temp_file_normal_operation();
1113611264
}
1113711265

1113811266
int main(void)

parse.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -486,8 +486,7 @@ static void hashmap_put(HashMap *map, char *key, int keylen, void *val)
486486
ent->key = key;
487487
ent->keylen = keylen;
488488
ent->val = val;
489-
if (ent->key != TOMBSTONE)
490-
map->used++;
489+
map->used++;
491490
}
492491

493492
static void hashmap_delete2(HashMap *map, char *key, int keylen)
@@ -503,6 +502,7 @@ static void hashmap_delete2(HashMap *map, char *key, int keylen)
503502
ent->keylen == keylen && !memcmp(ent->key, key, keylen))
504503
{
505504
ent->key = TOMBSTONE;
505+
map->used--;
506506
return;
507507
}
508508
if (!ent->key)

prism.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2820,6 +2820,7 @@ static void free_argv(char **argv)
28202820
static int make_temp_file(char *buf, size_t bufsize, const char *prefix, int suffix_len,
28212821
const char *source_adjacent)
28222822
{
2823+
int n;
28232824
if (source_adjacent)
28242825
{
28252826
const char *slash = strrchr(source_adjacent, '/');
@@ -2831,14 +2832,16 @@ static int make_temp_file(char *buf, size_t bufsize, const char *prefix, int suf
28312832
if (slash)
28322833
{
28332834
int dir_len = (int)(slash - source_adjacent);
2834-
snprintf(buf, bufsize, "%.*s/.%s.XXXXXX.c", dir_len, source_adjacent, slash + 1);
2835+
n = snprintf(buf, bufsize, "%.*s/.%s.XXXXXX.c", dir_len, source_adjacent, slash + 1);
28352836
}
28362837
else
2837-
snprintf(buf, bufsize, ".%s.XXXXXX.c", source_adjacent);
2838+
n = snprintf(buf, bufsize, ".%s.XXXXXX.c", source_adjacent);
28382839
suffix_len = 2; // .c suffix
28392840
}
28402841
else
2841-
snprintf(buf, bufsize, "%s%s", get_tmp_dir(), prefix);
2842+
n = snprintf(buf, bufsize, "%s%s", get_tmp_dir(), prefix);
2843+
if (n < 0 || (size_t)n >= bufsize)
2844+
return -1; // path truncated, XXXXXX template would be corrupted
28422845
#if defined(_WIN32)
28432846
if (_mktemp_s(buf, bufsize) != 0)
28442847
return -1;

0 commit comments

Comments
 (0)