Files
INF6B/niacin/cpp/vm_core.c

285 lines
7.5 KiB
C

#include "vm_types.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
// Safe memory allocation with overflow checking
static void* safe_malloc(size_t size) {
if (size == 0 || size > SIZE_MAX / 2) return NULL;
void* ptr = malloc(size);
if (!ptr) {
fprintf(stderr, "Memory allocation failed\n");
exit(EXIT_FAILURE);
}
return ptr;
}
static void* safe_realloc(void* ptr, size_t size) {
if (size == 0 || size > SIZE_MAX / 2) return NULL;
void* new_ptr = realloc(ptr, size);
if (!new_ptr) {
fprintf(stderr, "Memory reallocation failed\n");
exit(EXIT_FAILURE);
}
return new_ptr;
}
// Initialize VM with security limits
vm_t* vm_create(uint8_t* bytecode, size_t size) {
vm_t* vm = safe_malloc(sizeof(vm_t));
memset(vm, 0, sizeof(vm_t));
vm->bytecode = bytecode;
vm->bytecode_size = size;
vm->ip = 0;
vm->halted = false;
// Security limits
vm->max_stack_size = 65536;
vm->max_call_depth = 256;
vm->current_call_depth = 0;
return vm;
}
// Safe bytecode reading with bounds checking
static bool read_u8(vm_t* vm, uint8_t* result) {
if (vm->ip >= vm->bytecode_size) return false;
*result = vm->bytecode[vm->ip++];
return true;
}
static bool read_u16(vm_t* vm, uint16_t* result) {
if (vm->ip + 2 > vm->bytecode_size) return false;
memcpy(result, &vm->bytecode[vm->ip], 2);
vm->ip += 2;
return true;
}
static bool read_u32(vm_t* vm, uint32_t* result) {
if (vm->ip + 4 > vm->bytecode_size) return false;
memcpy(result, &vm->bytecode[vm->ip], 4);
vm->ip += 4;
return true;
}
static bool read_i32(vm_t* vm, int32_t* result) {
if (vm->ip + 4 > vm->bytecode_size) return false;
memcpy(result, &vm->bytecode[vm->ip], 4);
vm->ip += 4;
return true;
}
static bool read_f32(vm_t* vm, float* result) {
if (vm->ip + 4 > vm->bytecode_size) return false;
memcpy(result, &vm->bytecode[vm->ip], 4);
vm->ip += 4;
return true;
}
// Stack operations with bounds checking
static bool stack_push(frame_t* frame, value_t value) {
if (frame->stack_size >= frame->stack_capacity) {
size_t new_capacity = frame->stack_capacity * 2;
if (new_capacity == 0) new_capacity = 16;
value_t* new_stack = safe_realloc(frame->stack, new_capacity * sizeof(value_t));
if (!new_stack) return false;
frame->stack = new_stack;
frame->stack_capacity = new_capacity;
}
frame->stack[frame->stack_size++] = value;
return true;
}
static bool stack_pop(frame_t* frame, value_t* result) {
if (frame->stack_size == 0) return false;
*result = frame->stack[--frame->stack_size];
return true;
}
// Type checking and conversion
static bool type_check_binary(value_t a, value_t b, type_code_t* result_type) {
// Simple type checking - in real implementation, add proper type promotion
if (a.type == b.type) {
*result_type = a.type;
return true;
}
return false;
}
static int32_t value_to_int(value_t val) {
switch (val.type) {
case TYPE_I8: return val.data.i8;
case TYPE_U8: return val.data.u8;
case TYPE_I16: return val.data.i16;
case TYPE_U16: return val.data.u16;
case TYPE_I32: return val.data.i32;
case TYPE_U32: return (int32_t)val.data.u32;
case TYPE_BOOL: return val.data.boolean ? 1 : 0;
case TYPE_CHAR: return (int32_t)val.data.character;
default: return 0;
}
}
static float value_to_float(value_t val) {
switch (val.type) {
case TYPE_F32: return val.data.f32;
case TYPE_I32: return (float)val.data.i32;
case TYPE_U32: return (float)val.data.u32;
default: return 0.0f;
}
}
// Bytecode validation
bool vm_validate_bytecode(vm_t* vm) {
// Check magic number
if (vm->bytecode_size < 4 || memcmp(vm->bytecode, "POPC", 4) != 0) {
return false;
}
// Add more validation as needed
return true;
}
// Execute single instruction with full error checking
bool vm_execute_instruction(vm_t* vm) {
if (vm->halted || vm->ip >= vm->bytecode_size) return false;
uint8_t opcode;
if (!read_u8(vm, &opcode)) return false;
frame_t* frame = vm->current_frame;
if (!frame) return false;
switch (opcode) {
case OP_PUSH_CONST: {
uint32_t const_idx;
if (!read_u32(vm, &const_idx) || const_idx >= vm->constants_count) {
return false;
}
if (!stack_push(frame, vm->constants[const_idx])) return false;
break;
}
case OP_PUSH_INT: {
uint8_t width;
int32_t value;
if (!read_u8(vm, &width) || !read_i32(vm, &value)) return false;
value_t val = { 0 };
switch (width) {
case 8: val.type = TYPE_I8; val.data.i8 = (int8_t)value; break;
case 16: val.type = TYPE_I16; val.data.i16 = (int16_t)value; break;
case 32: val.type = TYPE_I32; val.data.i32 = value; break;
default: return false;
}
if (!stack_push(frame, val)) return false;
break;
}
case OP_ADD: {
value_t a, b;
if (!stack_pop(frame, &a) || !stack_pop(frame, &b)) return false;
type_code_t result_type;
if (!type_check_binary(a, b, &result_type)) return false;
value_t result = { 0 };
result.type = result_type;
if (result_type == TYPE_F32) {
result.data.f32 = value_to_float(b) + value_to_float(a);
}
else {
result.data.i32 = value_to_int(b) + value_to_int(a);
}
if (!stack_push(frame, result)) return false;
break;
}
case OP_CMP_EQ: {
value_t a, b;
if (!stack_pop(frame, &a) || !stack_pop(frame, &b)) return false;
value_t result = { 0 };
result.type = TYPE_BOOL;
result.data.boolean = (value_to_int(a) == value_to_int(b));
if (!stack_push(frame, result)) return false;
break;
}
case OP_JMP: {
int32_t offset;
if (!read_i32(vm, &offset)) return false;
// Check for negative jumps (security)
if (offset < 0 && (size_t)(-offset) > vm->ip) return false;
if (vm->ip + offset >= vm->bytecode_size) return false;
vm->ip += offset;
break;
}
case OP_PRINT: {
value_t val;
if (!stack_pop(frame, &val)) return false;
switch (val.type) {
case TYPE_I32: printf("%d\n", val.data.i32); break;
case TYPE_F32: printf("%f\n", val.data.f32); break;
case TYPE_BOOL: printf("%s\n", val.data.boolean ? "true" : "false"); break;
case TYPE_STR: printf("%s\n", val.data.string); break;
default: printf("<unknown>\n"); break;
}
break;
}
case OP_HALT:
vm->halted = true;
break;
default:
fprintf(stderr, "Unknown opcode: 0x%02x\n", opcode);
return false;
}
return true;
}
// Main execution loop
void vm_execute(vm_t* vm) {
while (!vm->halted && vm_execute_instruction(vm)) {
// Continue execution
}
}
// Cleanup
void vm_destroy(vm_t* vm) {
if (!vm) return;
// Free constants
for (size_t i = 0; i < vm->constants_count; i++) {
if (vm->constants[i].type == TYPE_STR && vm->constants[i].data.string) {
free(vm->constants[i].data.string);
}
}
free(vm->constants);
// Free functions
free(vm->functions);
// Free frames (simplified - in real impl, walk call stack)
if (vm->current_frame) {
free(vm->current_frame->locals);
free(vm->current_frame->stack);
free(vm->current_frame);
}
free(vm);
}