Skip to content

Commit 554be67

Browse files
committed
Fix critical memory allocation error handling
This adds proper NULL checks for all critical malloc/calloc/mpool_alloc calls that previously relied solely on assert(), which compiles out in production builds with -DNDEBUG. It also fixes critical JIT initialization error handling and syscall memory safety issues.
1 parent 1a06240 commit 554be67

File tree

7 files changed

+162
-49
lines changed

7 files changed

+162
-49
lines changed

src/cache.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,17 @@ void *cache_put(cache_t *cache, uint32_t key, void *value)
285285
}
286286

287287
cache_entry_t *new_entry = calloc(1, sizeof(cache_entry_t));
288+
if (unlikely(!new_entry)) {
289+
/* Allocation failed - restore replaced entry if exists */
290+
if (replaced) {
291+
replaced->alive = true;
292+
list_del_init(&replaced->list);
293+
list_add(&replaced->list, &cache->list);
294+
cache->size++;
295+
cache->ghost_list_size--;
296+
}
297+
return NULL;
298+
}
288299
assert(new_entry);
289300

290301
INIT_LIST_HEAD(&new_entry->list);

src/emulate.c

Lines changed: 70 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,8 @@ HASH_FUNC_IMPL(map_hash, BLOCK_MAP_CAPACITY_BITS, 1 << BLOCK_MAP_CAPACITY_BITS)
273273
static block_t *block_alloc(riscv_t *rv)
274274
{
275275
block_t *block = mpool_alloc(rv->block_mp);
276+
if (unlikely(!block))
277+
return NULL;
276278
assert(block);
277279
block->n_insn = 0;
278280
#if RV32_HAS(JIT)
@@ -619,13 +621,15 @@ FORCE_INLINE bool insn_is_indirect_branch(uint8_t opcode)
619621
}
620622
}
621623

622-
static void block_translate(riscv_t *rv, block_t *block)
624+
static bool block_translate(riscv_t *rv, block_t *block)
623625
{
624626
retranslate:
625627
block->pc_start = block->pc_end = rv->PC;
626628

627629
rv_insn_t *prev_ir = NULL;
628630
rv_insn_t *ir = mpool_calloc(rv->block_ir_mp);
631+
if (unlikely(!ir))
632+
return false;
629633
block->ir_head = ir;
630634

631635
/* translate the basic block */
@@ -665,6 +669,8 @@ static void block_translate(riscv_t *rv, block_t *block)
665669
if (insn_is_branch(ir->opcode)) {
666670
if (insn_is_indirect_branch(ir->opcode)) {
667671
ir->branch_table = calloc(1, sizeof(branch_history_table_t));
672+
if (unlikely(!ir->branch_table))
673+
return false;
668674
assert(ir->branch_table);
669675
memset(ir->branch_table->PC, -1,
670676
sizeof(uint32_t) * HISTORY_SIZE);
@@ -673,36 +679,44 @@ static void block_translate(riscv_t *rv, block_t *block)
673679
}
674680

675681
ir = mpool_calloc(rv->block_ir_mp);
682+
if (unlikely(!ir))
683+
return false;
676684
}
677685

678686
assert(prev_ir);
679687
block->ir_tail = prev_ir;
680688
block->ir_tail->next = NULL;
689+
return true;
681690
}
682691

683692
#if RV32_HAS(MOP_FUSION)
684-
#define COMBINE_MEM_OPS(RW) \
685-
next_ir = ir->next; \
686-
count = 1; \
687-
while (1) { \
688-
if (next_ir->opcode != IIF(RW)(rv_insn_lw, rv_insn_sw)) \
689-
break; \
690-
count++; \
691-
if (!next_ir->next) \
692-
break; \
693-
next_ir = next_ir->next; \
694-
} \
695-
if (count > 1) { \
696-
ir->opcode = IIF(RW)(rv_insn_fuse4, rv_insn_fuse3); \
697-
ir->fuse = malloc(count * sizeof(opcode_fuse_t)); \
698-
assert(ir->fuse); \
699-
ir->imm2 = count; \
700-
memcpy(ir->fuse, ir, sizeof(opcode_fuse_t)); \
701-
ir->impl = dispatch_table[ir->opcode]; \
702-
next_ir = ir->next; \
703-
for (int j = 1; j < count; j++, next_ir = next_ir->next) \
704-
memcpy(ir->fuse + j, next_ir, sizeof(opcode_fuse_t)); \
705-
remove_next_nth_ir(rv, ir, block, count - 1); \
693+
#define COMBINE_MEM_OPS(RW) \
694+
next_ir = ir->next; \
695+
count = 1; \
696+
while (1) { \
697+
if (next_ir->opcode != IIF(RW)(rv_insn_lw, rv_insn_sw)) \
698+
break; \
699+
count++; \
700+
if (!next_ir->next) \
701+
break; \
702+
next_ir = next_ir->next; \
703+
} \
704+
if (count > 1) { \
705+
ir->opcode = IIF(RW)(rv_insn_fuse4, rv_insn_fuse3); \
706+
ir->fuse = malloc(count * sizeof(opcode_fuse_t)); \
707+
if (unlikely(!ir->fuse)) { \
708+
ir->opcode = IIF(RW)(rv_insn_lw, rv_insn_sw); \
709+
count = 1; /* Degrade to non-fused operation */ \
710+
} else { \
711+
assert(ir->fuse); \
712+
ir->imm2 = count; \
713+
memcpy(ir->fuse, ir, sizeof(opcode_fuse_t)); \
714+
ir->impl = dispatch_table[ir->opcode]; \
715+
next_ir = ir->next; \
716+
for (int j = 1; j < count; j++, next_ir = next_ir->next) \
717+
memcpy(ir->fuse + j, next_ir, sizeof(opcode_fuse_t)); \
718+
remove_next_nth_ir(rv, ir, block, count - 1); \
719+
} \
706720
}
707721

708722
static inline void remove_next_nth_ir(const riscv_t *rv,
@@ -762,16 +776,20 @@ static void match_pattern(riscv_t *rv, block_t *block)
762776
next_ir = next_ir->next;
763777
}
764778
if (count > 1) {
765-
ir->opcode = rv_insn_fuse1;
766779
ir->fuse = malloc(count * sizeof(opcode_fuse_t));
767-
assert(ir->fuse);
768-
ir->imm2 = count;
769-
memcpy(ir->fuse, ir, sizeof(opcode_fuse_t));
770-
ir->impl = dispatch_table[ir->opcode];
771-
next_ir = ir->next;
772-
for (int j = 1; j < count; j++, next_ir = next_ir->next)
773-
memcpy(ir->fuse + j, next_ir, sizeof(opcode_fuse_t));
774-
remove_next_nth_ir(rv, ir, block, count - 1);
780+
if (likely(ir->fuse)) {
781+
ir->opcode = rv_insn_fuse1;
782+
assert(ir->fuse);
783+
ir->imm2 = count;
784+
memcpy(ir->fuse, ir, sizeof(opcode_fuse_t));
785+
ir->impl = dispatch_table[ir->opcode];
786+
next_ir = ir->next;
787+
for (int j = 1; j < count; j++, next_ir = next_ir->next)
788+
memcpy(ir->fuse + j, next_ir,
789+
sizeof(opcode_fuse_t));
790+
remove_next_nth_ir(rv, ir, block, count - 1);
791+
}
792+
/* If malloc failed, degrade gracefully to non-fused ops */
775793
}
776794
break;
777795
}
@@ -803,15 +821,18 @@ static void match_pattern(riscv_t *rv, block_t *block)
803821
}
804822
if (count > 1) {
805823
ir->fuse = malloc(count * sizeof(opcode_fuse_t));
806-
assert(ir->fuse);
807-
memcpy(ir->fuse, ir, sizeof(opcode_fuse_t));
808-
ir->opcode = rv_insn_fuse5;
809-
ir->imm2 = count;
810-
ir->impl = dispatch_table[ir->opcode];
811-
next_ir = ir->next;
812-
for (int j = 1; j < count; j++, next_ir = next_ir->next)
813-
memcpy(ir->fuse + j, next_ir, sizeof(opcode_fuse_t));
814-
remove_next_nth_ir(rv, ir, block, count - 1);
824+
if (likely(ir->fuse)) {
825+
assert(ir->fuse);
826+
memcpy(ir->fuse, ir, sizeof(opcode_fuse_t));
827+
ir->opcode = rv_insn_fuse5;
828+
ir->imm2 = count;
829+
ir->impl = dispatch_table[ir->opcode];
830+
next_ir = ir->next;
831+
for (int j = 1; j < count; j++, next_ir = next_ir->next)
832+
memcpy(ir->fuse + j, next_ir, sizeof(opcode_fuse_t));
833+
remove_next_nth_ir(rv, ir, block, count - 1);
834+
}
835+
/* If malloc failed, degrade gracefully to non-fused ops */
815836
}
816837
break;
817838
}
@@ -881,8 +902,11 @@ static block_t *block_find_or_translate(riscv_t *rv)
881902
#endif
882903
/* allocate a new block */
883904
next_blk = block_alloc(rv);
905+
if (unlikely(!next_blk))
906+
return NULL;
884907

885-
block_translate(rv, next_blk);
908+
if (unlikely(!block_translate(rv, next_blk)))
909+
return NULL;
886910

887911
#if RV32_HAS(JIT) && RV32_HAS(SYSTEM)
888912
/*
@@ -1129,6 +1153,10 @@ void rv_step(void *arg)
11291153
else if (!block->compiled && block->n_invoke >= THRESHOLD) {
11301154
block->compiled = true;
11311155
queue_entry_t *entry = malloc(sizeof(queue_entry_t));
1156+
if (unlikely(!entry)) {
1157+
/* Malloc failed - degrade to tier-1 JIT, don't queue for T2C */
1158+
continue;
1159+
}
11321160
entry->block = block;
11331161
pthread_mutex_lock(&rv->wait_queue_lock);
11341162
list_add(&entry->list, &rv->wait_queue);

src/io.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ memory_t *memory_new(uint32_t size)
2323
return NULL;
2424

2525
memory_t *mem = malloc(sizeof(memory_t));
26+
if (!mem)
27+
return NULL;
2628
assert(mem);
2729
#if HAVE_MMAP
2830
data_memory_base = mmap(NULL, size, PROT_READ | PROT_WRITE,

src/jit.c

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2330,7 +2330,10 @@ void jit_translate(riscv_t *rv, block_t *block)
23302330
struct jit_state *jit_state_init(size_t size)
23312331
{
23322332
struct jit_state *state = malloc(sizeof(struct jit_state));
2333+
if (!state)
2334+
return NULL;
23332335
assert(state);
2336+
23342337
state->offset = 0;
23352338
state->size = size;
23362339
state->buf = mmap(0, size, PROT_READ | PROT_WRITE | PROT_EXEC,
@@ -2340,13 +2343,32 @@ struct jit_state *jit_state_init(size_t size)
23402343
#endif
23412344
,
23422345
-1, 0);
2343-
state->n_blocks = 0;
2346+
if (state->buf == MAP_FAILED) {
2347+
free(state);
2348+
return NULL;
2349+
}
23442350
assert(state->buf != MAP_FAILED);
2351+
2352+
state->n_blocks = 0;
23452353
set_reset(&state->set);
23462354
reset_reg();
23472355
prepare_translate(state);
2356+
23482357
state->offset_map = calloc(MAX_BLOCKS, sizeof(struct offset_map));
2358+
if (!state->offset_map) {
2359+
munmap(state->buf, state->size);
2360+
free(state);
2361+
return NULL;
2362+
}
2363+
23492364
state->jumps = calloc(MAX_JUMPS, sizeof(struct jump));
2365+
if (!state->jumps) {
2366+
free(state->offset_map);
2367+
munmap(state->buf, state->size);
2368+
free(state);
2369+
return NULL;
2370+
}
2371+
23502372
return state;
23512373
}
23522374

src/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ static bool parse_args(int argc, char **args)
190190
return true;
191191
}
192192

193-
static void dump_test_signature(const char *prog_name)
193+
static void dump_test_signature(const char UNUSED *prog_name)
194194
{
195195
elf_t *elf = elf_new();
196196
assert(elf && elf_open(elf, prog_name));

src/riscv.c

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,8 @@ riscv_t *rv_create(riscv_user_t rv_attr)
510510
assert(rv_attr);
511511

512512
riscv_t *rv = calloc(1, sizeof(riscv_t));
513+
if (!rv)
514+
return NULL;
513515
assert(rv);
514516

515517
#if RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER)
@@ -578,7 +580,7 @@ riscv_t *rv_create(riscv_user_t rv_attr)
578580
assert(elf_load(elf, attr->mem));
579581

580582
/* set the entry pc */
581-
const struct Elf32_Ehdr *hdr = get_elf_header(elf);
583+
const struct Elf32_Ehdr UNUSED *hdr = get_elf_header(elf);
582584
assert(rv_set_pc(rv, hdr->e_entry));
583585

584586
elf_delete(elf);
@@ -724,11 +726,22 @@ riscv_t *rv_create(riscv_user_t rv_attr)
724726
#else
725727
INIT_LIST_HEAD(&rv->block_list);
726728
rv->jit_state = jit_state_init(CODE_CACHE_SIZE);
729+
if (!rv->jit_state) {
730+
rv_log_fatal("Failed to initialize JIT state");
731+
goto fail_jit_state;
732+
}
727733
rv->block_cache = cache_create(BLOCK_MAP_CAPACITY_BITS);
728-
assert(rv->block_cache);
734+
if (!rv->block_cache) {
735+
rv_log_fatal("Failed to create block cache");
736+
goto fail_block_cache;
737+
}
729738
#if RV32_HAS(T2C)
730739
rv->quit = false;
731740
rv->jit_cache = jit_cache_init();
741+
if (!rv->jit_cache) {
742+
rv_log_fatal("Failed to initialize JIT cache");
743+
goto fail_jit_cache;
744+
}
732745
/* prepare wait queue. */
733746
pthread_mutex_init(&rv->wait_queue_lock, NULL);
734747
pthread_mutex_init(&rv->cache_lock, NULL);
@@ -739,6 +752,22 @@ riscv_t *rv_create(riscv_user_t rv_attr)
739752
#endif
740753

741754
return rv;
755+
756+
#if RV32_HAS(JIT)
757+
#if RV32_HAS(T2C)
758+
fail_jit_cache:
759+
cache_free(rv->block_cache);
760+
#endif
761+
fail_block_cache:
762+
jit_state_exit(rv->jit_state);
763+
fail_jit_state:
764+
mpool_destroy(rv->block_ir_mp);
765+
mpool_destroy(rv->block_mp);
766+
map_delete(attr->fd_map);
767+
memory_delete(attr->mem);
768+
free(rv);
769+
return NULL;
770+
#endif
742771
}
743772

744773
#if !RV32_HAS(SYSTEM) || (RV32_HAS(SYSTEM) && RV32_HAS(ELF_LOADER))
@@ -753,7 +782,7 @@ static void rv_run_and_trace(riscv_t *rv)
753782
assert(attr && attr->data.user.elf_program);
754783
attr->cycle_per_step = 1;
755784

756-
const char *prog_name = attr->data.user.elf_program;
785+
const char UNUSED *prog_name = attr->data.user.elf_program;
757786
elf_t *elf = elf_new();
758787
assert(elf && elf_open(elf, prog_name));
759788

src/syscall.c

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,22 +362,43 @@ static void syscall_open(riscv_t *rv)
362362
uint32_t flags = rv_get_reg(rv, rv_reg_a1);
363363
uint32_t mode = rv_get_reg(rv, rv_reg_a2);
364364

365-
/* read name from runtime memory */
366-
const size_t name_len = strlen((char *) attr->mem->mem_base + name);
365+
/* read name from runtime memory with bounds checking */
366+
if (name >= attr->mem->mem_size) {
367+
rv_set_reg(rv, rv_reg_a0, -1);
368+
return;
369+
}
370+
371+
/* Calculate safe maximum length to prevent reading beyond memory */
372+
const size_t max_len = attr->mem->mem_size - name;
373+
const char *name_ptr = (char *) attr->mem->mem_base + name;
374+
375+
/* Use strnlen to safely find string length within bounds */
376+
const size_t name_len = strnlen(name_ptr, max_len);
377+
if (name_len == max_len) {
378+
/* No null terminator found within bounds */
379+
rv_set_reg(rv, rv_reg_a0, -1);
380+
return;
381+
}
367382
char *name_str = malloc(name_len + 1);
383+
if (!name_str) {
384+
rv_set_reg(rv, rv_reg_a0, -1);
385+
return;
386+
}
368387
assert(name_str);
369388
name_str[name_len] = '\0';
370389
memory_read(attr->mem, (uint8_t *) name_str, name, name_len);
371390

372391
/* open the file */
373392
const char *mode_str = get_mode_str(flags, mode);
374393
if (!mode_str) {
394+
free(name_str);
375395
rv_set_reg(rv, rv_reg_a0, -1);
376396
return;
377397
}
378398

379399
FILE *handle = fopen(name_str, mode_str);
380400
if (!handle) {
401+
free(name_str);
381402
rv_set_reg(rv, rv_reg_a0, -1);
382403
return;
383404
}

0 commit comments

Comments
 (0)