diff --git a/src/cache.c b/src/cache.c index 90da4221..8dcbbf0e 100644 --- a/src/cache.c +++ b/src/cache.c @@ -147,6 +147,10 @@ static inline void hlist_del_init(struct hlist_node *n) cache_t *cache_create(uint32_t size_bits) { + /* Prevent integer overflow in 1 << size_bits */ + if (size_bits >= 32) + return NULL; + cache_t *cache = malloc(sizeof(cache_t)); if (!cache) return NULL; @@ -160,16 +164,23 @@ cache_t *cache_create(uint32_t size_bits) cache->ghost_list_size = 0; cache->capacity = cache_size; - cache->map.ht_list_head = malloc(cache_size * sizeof(struct hlist_head)); - if (!cache->map.ht_list_head) { - free(cache); - return NULL; - } + /* Check for overflow in size calculation */ + size_t alloc_size = cache_size * sizeof(struct hlist_head); + if (alloc_size / sizeof(struct hlist_head) != cache_size) + goto fail_cache; + + cache->map.ht_list_head = malloc(alloc_size); + if (!cache->map.ht_list_head) + goto fail_cache; for (uint32_t i = 0; i < cache_size; i++) INIT_HLIST_HEAD(&cache->map.ht_list_head[i]); return cache; + +fail_cache: + free(cache); + return NULL; } void *cache_get(const cache_t *cache, uint32_t key, bool update) @@ -285,6 +296,17 @@ void *cache_put(cache_t *cache, uint32_t key, void *value) } cache_entry_t *new_entry = calloc(1, sizeof(cache_entry_t)); + if (unlikely(!new_entry)) { + /* Allocation failed - restore replaced entry if exists */ + if (replaced) { + replaced->alive = true; + list_del_init(&replaced->list); + list_add(&replaced->list, &cache->list); + cache->size++; + cache->ghost_list_size--; + } + return NULL; + } assert(new_entry); INIT_LIST_HEAD(&new_entry->list); diff --git a/src/elf.c b/src/elf.c index 27668148..7c70af47 100644 --- a/src/elf.c +++ b/src/elf.c @@ -274,14 +274,14 @@ bool elf_load(elf_t *e, memory_t *mem) /* memcpy required range */ const int to_copy = min(phdr->p_memsz, phdr->p_filesz); - if (to_copy) - memory_write(mem, phdr->p_vaddr, e->raw_data + phdr->p_offset, - to_copy); + if (to_copy && !memory_write(mem, phdr->p_vaddr, + e->raw_data + phdr->p_offset, to_copy)) + return false; /* zero fill required range */ const int to_zero = max(phdr->p_memsz, phdr->p_filesz) - to_copy; - if (to_zero) - memory_fill(mem, phdr->p_vaddr + to_copy, to_zero, 0); + if (to_zero && !memory_fill(mem, phdr->p_vaddr + to_copy, to_zero, 0)) + return false; } return true; diff --git a/src/emulate.c b/src/emulate.c index d9c72c81..58585f8a 100644 --- a/src/emulate.c +++ b/src/emulate.c @@ -273,6 +273,8 @@ HASH_FUNC_IMPL(map_hash, BLOCK_MAP_CAPACITY_BITS, 1 << BLOCK_MAP_CAPACITY_BITS) static block_t *block_alloc(riscv_t *rv) { block_t *block = mpool_alloc(rv->block_mp); + if (unlikely(!block)) + return NULL; assert(block); block->n_insn = 0; #if RV32_HAS(JIT) @@ -619,13 +621,15 @@ FORCE_INLINE bool insn_is_indirect_branch(uint8_t opcode) } } -static void block_translate(riscv_t *rv, block_t *block) +static bool block_translate(riscv_t *rv, block_t *block) { retranslate: block->pc_start = block->pc_end = rv->PC; rv_insn_t *prev_ir = NULL; rv_insn_t *ir = mpool_calloc(rv->block_ir_mp); + if (unlikely(!ir)) + return false; block->ir_head = ir; /* translate the basic block */ @@ -665,6 +669,8 @@ static void block_translate(riscv_t *rv, block_t *block) if (insn_is_branch(ir->opcode)) { if (insn_is_indirect_branch(ir->opcode)) { ir->branch_table = calloc(1, sizeof(branch_history_table_t)); + if (unlikely(!ir->branch_table)) + return false; assert(ir->branch_table); memset(ir->branch_table->PC, -1, sizeof(uint32_t) * HISTORY_SIZE); @@ -673,36 +679,44 @@ static void block_translate(riscv_t *rv, block_t *block) } ir = mpool_calloc(rv->block_ir_mp); + if (unlikely(!ir)) + return false; } assert(prev_ir); block->ir_tail = prev_ir; block->ir_tail->next = NULL; + return true; } #if RV32_HAS(MOP_FUSION) -#define COMBINE_MEM_OPS(RW) \ - next_ir = ir->next; \ - count = 1; \ - while (1) { \ - if (next_ir->opcode != IIF(RW)(rv_insn_lw, rv_insn_sw)) \ - break; \ - count++; \ - if (!next_ir->next) \ - break; \ - next_ir = next_ir->next; \ - } \ - if (count > 1) { \ - ir->opcode = IIF(RW)(rv_insn_fuse4, rv_insn_fuse3); \ - ir->fuse = malloc(count * sizeof(opcode_fuse_t)); \ - assert(ir->fuse); \ - ir->imm2 = count; \ - memcpy(ir->fuse, ir, sizeof(opcode_fuse_t)); \ - ir->impl = dispatch_table[ir->opcode]; \ - next_ir = ir->next; \ - for (int j = 1; j < count; j++, next_ir = next_ir->next) \ - memcpy(ir->fuse + j, next_ir, sizeof(opcode_fuse_t)); \ - remove_next_nth_ir(rv, ir, block, count - 1); \ +#define COMBINE_MEM_OPS(RW) \ + next_ir = ir->next; \ + count = 1; \ + while (1) { \ + if (next_ir->opcode != IIF(RW)(rv_insn_lw, rv_insn_sw)) \ + break; \ + count++; \ + if (!next_ir->next) \ + break; \ + next_ir = next_ir->next; \ + } \ + if (count > 1) { \ + ir->opcode = IIF(RW)(rv_insn_fuse4, rv_insn_fuse3); \ + ir->fuse = malloc(count * sizeof(opcode_fuse_t)); \ + if (unlikely(!ir->fuse)) { \ + ir->opcode = IIF(RW)(rv_insn_lw, rv_insn_sw); \ + count = 1; /* Degrade to non-fused operation */ \ + } else { \ + assert(ir->fuse); \ + ir->imm2 = count; \ + memcpy(ir->fuse, ir, sizeof(opcode_fuse_t)); \ + ir->impl = dispatch_table[ir->opcode]; \ + next_ir = ir->next; \ + for (int j = 1; j < count; j++, next_ir = next_ir->next) \ + memcpy(ir->fuse + j, next_ir, sizeof(opcode_fuse_t)); \ + remove_next_nth_ir(rv, ir, block, count - 1); \ + } \ } 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) next_ir = next_ir->next; } if (count > 1) { - ir->opcode = rv_insn_fuse1; ir->fuse = malloc(count * sizeof(opcode_fuse_t)); - assert(ir->fuse); - ir->imm2 = count; - memcpy(ir->fuse, ir, sizeof(opcode_fuse_t)); - ir->impl = dispatch_table[ir->opcode]; - next_ir = ir->next; - for (int j = 1; j < count; j++, next_ir = next_ir->next) - memcpy(ir->fuse + j, next_ir, sizeof(opcode_fuse_t)); - remove_next_nth_ir(rv, ir, block, count - 1); + if (likely(ir->fuse)) { + ir->opcode = rv_insn_fuse1; + assert(ir->fuse); + ir->imm2 = count; + memcpy(ir->fuse, ir, sizeof(opcode_fuse_t)); + ir->impl = dispatch_table[ir->opcode]; + next_ir = ir->next; + for (int j = 1; j < count; j++, next_ir = next_ir->next) + memcpy(ir->fuse + j, next_ir, + sizeof(opcode_fuse_t)); + remove_next_nth_ir(rv, ir, block, count - 1); + } + /* If malloc failed, degrade gracefully to non-fused ops */ } break; } @@ -803,15 +821,18 @@ static void match_pattern(riscv_t *rv, block_t *block) } if (count > 1) { ir->fuse = malloc(count * sizeof(opcode_fuse_t)); - assert(ir->fuse); - memcpy(ir->fuse, ir, sizeof(opcode_fuse_t)); - ir->opcode = rv_insn_fuse5; - ir->imm2 = count; - ir->impl = dispatch_table[ir->opcode]; - next_ir = ir->next; - for (int j = 1; j < count; j++, next_ir = next_ir->next) - memcpy(ir->fuse + j, next_ir, sizeof(opcode_fuse_t)); - remove_next_nth_ir(rv, ir, block, count - 1); + if (likely(ir->fuse)) { + assert(ir->fuse); + memcpy(ir->fuse, ir, sizeof(opcode_fuse_t)); + ir->opcode = rv_insn_fuse5; + ir->imm2 = count; + ir->impl = dispatch_table[ir->opcode]; + next_ir = ir->next; + for (int j = 1; j < count; j++, next_ir = next_ir->next) + memcpy(ir->fuse + j, next_ir, sizeof(opcode_fuse_t)); + remove_next_nth_ir(rv, ir, block, count - 1); + } + /* If malloc failed, degrade gracefully to non-fused ops */ } break; } @@ -881,8 +902,11 @@ static block_t *block_find_or_translate(riscv_t *rv) #endif /* allocate a new block */ next_blk = block_alloc(rv); + if (unlikely(!next_blk)) + return NULL; - block_translate(rv, next_blk); + if (unlikely(!block_translate(rv, next_blk))) + return NULL; #if RV32_HAS(JIT) && RV32_HAS(SYSTEM) /* @@ -1078,6 +1102,12 @@ void rv_step(void *arg) */ block_t *block = block_find_or_translate(rv); /* by now, a block should be available */ + if (unlikely(!block)) { + rv_log_fatal("Failed to allocate or translate block at PC=0x%08x", + rv->PC); + rv->halt = true; + return; + } assert(block); #if RV32_HAS(JIT) && RV32_HAS(SYSTEM) @@ -1129,6 +1159,11 @@ void rv_step(void *arg) else if (!block->compiled && block->n_invoke >= THRESHOLD) { block->compiled = true; queue_entry_t *entry = malloc(sizeof(queue_entry_t)); + if (unlikely(!entry)) { + /* Malloc failed - reset compiled flag to allow retry later */ + block->compiled = false; + continue; + } entry->block = block; pthread_mutex_lock(&rv->wait_queue_lock); list_add(&entry->list, &rv->wait_queue); @@ -1378,16 +1413,38 @@ void ecall_handler(riscv_t *rv) void memset_handler(riscv_t *rv) { memory_t *m = PRIV(rv)->mem; - memset((char *) m->mem_base + rv->X[rv_reg_a0], rv->X[rv_reg_a1], - rv->X[rv_reg_a2]); + uint32_t dest = rv->X[rv_reg_a0]; + uint32_t value = rv->X[rv_reg_a1]; + uint32_t count = rv->X[rv_reg_a2]; + + /* Bounds checking to prevent buffer overflow */ + if (dest >= m->mem_size || count > m->mem_size - dest) { + SET_CAUSE_AND_TVAL_THEN_TRAP(rv, STORE_MISALIGNED, dest); + return; + } + + memset((char *) m->mem_base + dest, value, count); rv->PC = rv->X[rv_reg_ra] & ~1U; } void memcpy_handler(riscv_t *rv) { memory_t *m = PRIV(rv)->mem; - memcpy((char *) m->mem_base + rv->X[rv_reg_a0], - (char *) m->mem_base + rv->X[rv_reg_a1], rv->X[rv_reg_a2]); + uint32_t dest = rv->X[rv_reg_a0]; + uint32_t src = rv->X[rv_reg_a1]; + uint32_t count = rv->X[rv_reg_a2]; + + /* Bounds checking to prevent buffer overflow */ + if (dest >= m->mem_size || count > m->mem_size - dest) { + SET_CAUSE_AND_TVAL_THEN_TRAP(rv, STORE_MISALIGNED, dest); + return; + } + if (src >= m->mem_size || count > m->mem_size - src) { + SET_CAUSE_AND_TVAL_THEN_TRAP(rv, LOAD_MISALIGNED, src); + return; + } + + memcpy((char *) m->mem_base + dest, (char *) m->mem_base + src, count); rv->PC = rv->X[rv_reg_ra] & ~1U; } diff --git a/src/io.c b/src/io.c index 2009e452..4ff325d3 100644 --- a/src/io.c +++ b/src/io.c @@ -23,6 +23,8 @@ memory_t *memory_new(uint32_t size) return NULL; memory_t *mem = malloc(sizeof(memory_t)); + if (!mem) + return NULL; assert(mem); #if HAVE_MMAP data_memory_base = mmap(NULL, size, PROT_READ | PROT_WRITE, diff --git a/src/io.h b/src/io.h index 2f9f537b..3104ec50 100644 --- a/src/io.h +++ b/src/io.h @@ -36,12 +36,16 @@ uint8_t memory_read_b(uint32_t addr); void memory_read(const memory_t *m, uint8_t *dst, uint32_t addr, uint32_t size); /* write a length of data to memory */ -static inline void memory_write(memory_t *m, +static inline bool memory_write(memory_t *m, uint32_t addr, const uint8_t *src, uint32_t size) { + /* Bounds checking to prevent buffer overflow */ + if (addr >= m->mem_size || size > m->mem_size - addr) + return false; memcpy(m->mem_base + addr, src, size); + return true; } /* write a word to memory */ @@ -54,10 +58,14 @@ void memory_write_s(uint32_t addr, const uint8_t *src); void memory_write_b(uint32_t addr, const uint8_t *src); /* write a length of certain value to memory */ -static inline void memory_fill(memory_t *m, +static inline bool memory_fill(memory_t *m, uint32_t addr, uint32_t size, uint8_t val) { + /* Bounds checking to prevent buffer overflow */ + if (addr >= m->mem_size || size > m->mem_size - addr) + return false; memset(m->mem_base + addr, val, size); + return true; } diff --git a/src/jit.c b/src/jit.c index 5b799eec..158665d4 100644 --- a/src/jit.c +++ b/src/jit.c @@ -2330,7 +2330,10 @@ void jit_translate(riscv_t *rv, block_t *block) struct jit_state *jit_state_init(size_t size) { struct jit_state *state = malloc(sizeof(struct jit_state)); + if (!state) + return NULL; assert(state); + state->offset = 0; state->size = size; state->buf = mmap(0, size, PROT_READ | PROT_WRITE | PROT_EXEC, @@ -2340,13 +2343,32 @@ struct jit_state *jit_state_init(size_t size) #endif , -1, 0); - state->n_blocks = 0; + if (state->buf == MAP_FAILED) { + free(state); + return NULL; + } assert(state->buf != MAP_FAILED); + + state->n_blocks = 0; set_reset(&state->set); reset_reg(); prepare_translate(state); + state->offset_map = calloc(MAX_BLOCKS, sizeof(struct offset_map)); + if (!state->offset_map) { + munmap(state->buf, state->size); + free(state); + return NULL; + } + state->jumps = calloc(MAX_JUMPS, sizeof(struct jump)); + if (!state->jumps) { + free(state->offset_map); + munmap(state->buf, state->size); + free(state); + return NULL; + } + return state; } diff --git a/src/main.c b/src/main.c index d5729853..8e079a50 100644 --- a/src/main.c +++ b/src/main.c @@ -177,20 +177,33 @@ static bool parse_args(int argc, char **args) assert(getcwd(cwd_path, PATH_MAX)); char rel_path[PATH_MAX] = {0}; - memcpy(rel_path, args[0], strlen(args[0]) - 7 /* strlen("rv32emu") */); + size_t args0_len = strlen(args[0]); + /* Ensure args[0] is long enough before subtracting */ + if (args0_len > 7) { /* strlen("rv32emu") */ + size_t copy_len = args0_len - 7; + if (copy_len >= PATH_MAX) + copy_len = PATH_MAX - 1; + memcpy(rel_path, args[0], copy_len); + rel_path[copy_len] = '\0'; + } char *prog_basename = basename(opt_prog_name); - prof_out_file = malloc(strlen(cwd_path) + 1 + strlen(rel_path) + - strlen(prog_basename) + 5 + 1); + size_t total_len = strlen(cwd_path) + 1 + strlen(rel_path) + + strlen(prog_basename) + 5 + 1; + prof_out_file = malloc(total_len); + if (!prof_out_file) { + rv_log_error("Failed to allocate profiling output filename"); + return false; + } assert(prof_out_file); - sprintf(prof_out_file, "%s/%s%s.prof", cwd_path, rel_path, - prog_basename); + snprintf(prof_out_file, total_len, "%s/%s%s.prof", cwd_path, rel_path, + prog_basename); } return true; } -static void dump_test_signature(const char *prog_name) +static void dump_test_signature(const char UNUSED *prog_name) { elf_t *elf = elf_new(); assert(elf && elf_open(elf, prog_name)); diff --git a/src/mpool.c b/src/mpool.c index 9435537f..27b11154 100644 --- a/src/mpool.c +++ b/src/mpool.c @@ -2,6 +2,7 @@ * rv32emu is freely redistributable under the MIT License. See the file * "LICENSE" for information on usage and redistribution of this file. */ +#include #include #include #if HAVE_MMAP @@ -51,14 +52,27 @@ mpool_t *mpool_create(size_t pool_size, size_t chunk_size) new_mp->area.next = NULL; size_t pgsz = getpagesize(); + + /* Check for overflow in chunk_size + sizeof(memchunk_t) */ + if (chunk_size > SIZE_MAX - sizeof(memchunk_t)) + goto fail_mpool; + if (pool_size < chunk_size + sizeof(memchunk_t)) pool_size += sizeof(memchunk_t); + + /* Check for overflow in pool_size + pgsz - 1 */ + if (pool_size > SIZE_MAX - pgsz + 1) + goto fail_mpool; + size_t page_count = (pool_size + pgsz - 1) / pgsz; + + /* Check for overflow in page_count * pgsz */ + if (page_count > SIZE_MAX / pgsz) + goto fail_mpool; + char *p = mem_arena(page_count * pgsz); - if (!p) { - free(new_mp); - return NULL; - } + if (!p) + goto fail_mpool; new_mp->area.mapped = p; new_mp->page_count = page_count; @@ -73,6 +87,10 @@ mpool_t *mpool_create(size_t pool_size, size_t chunk_size) } cur->next = NULL; return new_mp; + +fail_mpool: + free(new_mp); + return NULL; } static void *mpool_extend(mpool_t *mp) @@ -84,7 +102,7 @@ static void *mpool_extend(mpool_t *mp) area_t *new_area = malloc(sizeof(area_t)); if (!new_area) - return NULL; + goto fail_area; new_area->mapped = p; new_area->next = NULL; @@ -105,6 +123,14 @@ static void *mpool_extend(mpool_t *mp) cur_area->next = new_area; return p; + +fail_area: +#if HAVE_MMAP + munmap(p, pool_size); +#else + free(p); +#endif + return NULL; } FORCE_INLINE void *mpool_alloc_helper(mpool_t *mp) diff --git a/src/riscv.c b/src/riscv.c index 5b43114d..dcaaa94a 100644 --- a/src/riscv.c +++ b/src/riscv.c @@ -510,6 +510,8 @@ riscv_t *rv_create(riscv_user_t rv_attr) assert(rv_attr); riscv_t *rv = calloc(1, sizeof(riscv_t)); + if (!rv) + return NULL; assert(rv); #if RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER) @@ -578,7 +580,7 @@ riscv_t *rv_create(riscv_user_t rv_attr) assert(elf_load(elf, attr->mem)); /* set the entry pc */ - const struct Elf32_Ehdr *hdr = get_elf_header(elf); + const struct Elf32_Ehdr UNUSED *hdr = get_elf_header(elf); assert(rv_set_pc(rv, hdr->e_entry)); elf_delete(elf); @@ -724,11 +726,22 @@ riscv_t *rv_create(riscv_user_t rv_attr) #else INIT_LIST_HEAD(&rv->block_list); rv->jit_state = jit_state_init(CODE_CACHE_SIZE); + if (!rv->jit_state) { + rv_log_fatal("Failed to initialize JIT state"); + goto fail_jit_state; + } rv->block_cache = cache_create(BLOCK_MAP_CAPACITY_BITS); - assert(rv->block_cache); + if (!rv->block_cache) { + rv_log_fatal("Failed to create block cache"); + goto fail_block_cache; + } #if RV32_HAS(T2C) rv->quit = false; rv->jit_cache = jit_cache_init(); + if (!rv->jit_cache) { + rv_log_fatal("Failed to initialize JIT cache"); + goto fail_jit_cache; + } /* prepare wait queue. */ pthread_mutex_init(&rv->wait_queue_lock, NULL); pthread_mutex_init(&rv->cache_lock, NULL); @@ -739,6 +752,23 @@ riscv_t *rv_create(riscv_user_t rv_attr) #endif return rv; + +#if RV32_HAS(JIT) +#if RV32_HAS(T2C) +fail_jit_cache: + cache_free(rv->block_cache); +#endif +fail_block_cache: + if (rv->jit_state) + jit_state_exit(rv->jit_state); +fail_jit_state: + mpool_destroy(rv->block_ir_mp); + mpool_destroy(rv->block_mp); + map_delete(attr->fd_map); + memory_delete(attr->mem); + free(rv); + return NULL; +#endif } #if !RV32_HAS(SYSTEM) || (RV32_HAS(SYSTEM) && RV32_HAS(ELF_LOADER)) @@ -753,7 +783,7 @@ static void rv_run_and_trace(riscv_t *rv) assert(attr && attr->data.user.elf_program); attr->cycle_per_step = 1; - const char *prog_name = attr->data.user.elf_program; + const char UNUSED *prog_name = attr->data.user.elf_program; elf_t *elf = elf_new(); assert(elf && elf_open(elf, prog_name)); @@ -911,7 +941,7 @@ void rv_reset(riscv_t *rv, riscv_word_t pc) /* argc */ uintptr_t *args_p = (uintptr_t *) args_top; - memory_write(mem, (uintptr_t) args_p, (void *) &argc, sizeof(int)); + assert(memory_write(mem, (uintptr_t) args_p, (void *) &argc, sizeof(int))); args_p++; /* args */ @@ -923,8 +953,8 @@ void rv_reset(riscv_t *rv, riscv_word_t pc) for (int i = 0; i < argc; i++) { const char *arg = args[i]; args_len = strlen(arg); - memory_write(mem, (uintptr_t) args_p, (void *) arg, - (args_len + 1) * sizeof(uint8_t)); + assert(memory_write(mem, (uintptr_t) args_p, (void *) arg, + (args_len + 1) * sizeof(uint8_t))); args_space[args_space_idx++] = args_len + 1; args_p = (uintptr_t *) ((uintptr_t) args_p + args_len + 1); args_len_total += args_len + 1; @@ -940,8 +970,9 @@ void rv_reset(riscv_t *rv, riscv_word_t pc) /* argc */ uintptr_t *sp = (uintptr_t *) stack_top; - memory_write(mem, (uintptr_t) sp, - (void *) (mem->mem_base + (uintptr_t) args_p), sizeof(int)); + assert(memory_write(mem, (uintptr_t) sp, + (void *) (mem->mem_base + (uintptr_t) args_p), + sizeof(int))); args_p++; /* keep argc and args[0] within one word due to RV32 ABI */ sp = (uintptr_t *) ((uint32_t *) sp + 1); @@ -949,11 +980,12 @@ void rv_reset(riscv_t *rv, riscv_word_t pc) /* args */ for (int i = 0; i < argc; i++) { uintptr_t offset = (uintptr_t) args_p; - memory_write(mem, (uintptr_t) sp, (void *) &offset, sizeof(uintptr_t)); + assert(memory_write(mem, (uintptr_t) sp, (void *) &offset, + sizeof(uintptr_t))); args_p = (uintptr_t *) ((uintptr_t) args_p + args_space[i]); sp = (uintptr_t *) ((uint32_t *) sp + 1); } - memory_fill(mem, (uintptr_t) sp, sizeof(uint32_t), 0); + assert(memory_fill(mem, (uintptr_t) sp, sizeof(uint32_t), 0)); /* reset sp pointing to argc */ rv->X[rv_reg_sp] = stack_top; diff --git a/src/syscall.c b/src/syscall.c index e3c4e400..3d49c7b6 100644 --- a/src/syscall.c +++ b/src/syscall.c @@ -330,14 +330,20 @@ static void syscall_read(riscv_t *rv) while (count > PREALLOC_SIZE) { size_t r = fread(tmp, 1, PREALLOC_SIZE, handle); - memory_write(attr->mem, buf + total_read, tmp, r); + if (!memory_write(attr->mem, buf + total_read, tmp, r)) { + rv_set_reg(rv, rv_reg_a0, -1); + return; + } count -= r; total_read += r; if (r != PREALLOC_SIZE) break; } size_t r = fread(tmp, 1, count, handle); - memory_write(attr->mem, buf + total_read, tmp, r); + if (!memory_write(attr->mem, buf + total_read, tmp, r)) { + rv_set_reg(rv, rv_reg_a0, -1); + return; + } total_read += r; if (total_read != rv_get_reg(rv, rv_reg_a2) && ferror(handle)) { /* error */ @@ -362,9 +368,28 @@ static void syscall_open(riscv_t *rv) uint32_t flags = rv_get_reg(rv, rv_reg_a1); uint32_t mode = rv_get_reg(rv, rv_reg_a2); - /* read name from runtime memory */ - const size_t name_len = strlen((char *) attr->mem->mem_base + name); + /* read name from runtime memory with bounds checking */ + if (name >= attr->mem->mem_size) { + rv_set_reg(rv, rv_reg_a0, -1); + return; + } + + /* Calculate safe maximum length to prevent reading beyond memory */ + const size_t max_len = attr->mem->mem_size - name; + const char *name_ptr = (char *) attr->mem->mem_base + name; + + /* Use strnlen to safely find string length within bounds */ + const size_t name_len = strnlen(name_ptr, max_len); + if (name_len == max_len) { + /* No null terminator found within bounds */ + rv_set_reg(rv, rv_reg_a0, -1); + return; + } char *name_str = malloc(name_len + 1); + if (!name_str) { + rv_set_reg(rv, rv_reg_a0, -1); + return; + } assert(name_str); name_str[name_len] = '\0'; memory_read(attr->mem, (uint8_t *) name_str, name, name_len); @@ -372,12 +397,14 @@ static void syscall_open(riscv_t *rv) /* open the file */ const char *mode_str = get_mode_str(flags, mode); if (!mode_str) { + free(name_str); rv_set_reg(rv, rv_reg_a0, -1); return; } FILE *handle = fopen(name_str, mode_str); if (!handle) { + free(name_str); rv_set_reg(rv, rv_reg_a0, -1); return; } diff --git a/src/syscall_sdl.c b/src/syscall_sdl.c index e07c26b3..ae95b37f 100644 --- a/src/syscall_sdl.c +++ b/src/syscall_sdl.c @@ -216,9 +216,10 @@ static submission_t submission_pop(riscv_t *rv) static void event_push(riscv_t *rv, event_t event) { vm_attr_t *attr = PRIV(rv); - memory_write(attr->mem, - event_queue.base + event_queue.end * sizeof(event_t), - (void *) &event, sizeof(event_t)); + if (!memory_write(attr->mem, + event_queue.base + event_queue.end * sizeof(event_t), + (void *) &event, sizeof(event_t))) + return; ++event_queue.end; event_queue.end &= queues_capacity - 1;