Security Model
Memory Safety via ARC
Braid uses Automatic Reference Counting (ARC) with cycle detection as its primary memory management strategy. Every heap-allocated object carries a reference count in its header:
struct Object {
ObjType type;
int ref_count;
struct Object* next;
bool marked;
};ARC ensures objects are freed immediately when their reference count reaches zero, preventing dangling pointers. The gc_retain() and gc_release() calls are inserted throughout the VM to manage ownership precisely.
No Undefined Behavior
Braid's Hindley-Milner type system prevents type confusion at compile time. The VM enforces type safety at runtime with #if VM_SAFE guards that validate every stack operation, array index, and field access:
#if VM_SAFE
if (current_vm->stack_top >= current_vm->stack + STACK_MAX) {
runtime_error("VM: Stack overflow on push.");
return;
}
#endifDivision by zero, stack underflow, and out-of-bounds constant access are all caught at runtime with descriptive error messages.
Safe FFI via Extern Declarations
Braid provides two FFI mechanisms with different safety profiles:
- extern fn — binds to C functions (e.g.,
extern fn sqrt(x: float) -> float); the programmer must ensure type signatures match - native fn — registered functions linked into the BraidVM runtime, wrapped with proper type marshalling
The module name validation in the VM (is_module_name_safe) prevents path traversal attacks by rejecting module names with special characters:
static bool is_module_name_safe(const char* module_name) {
if (!module_name || module_name[0] == '\0') return false;
for (const char* p = module_name; *p; p++) {
if (!(isalnum(*p) || *p == '_' || *p == '.'))
return false;
}
return true;
}Sandboxed compiler.eval()
Runtime code compilation via std.compiler runs in a controlled environment. The module resolution path is scoped to the current project directory, and only safe character sequences are allowed in module names.
No Arbitrary Code Execution
Braid does not allow arbitrary code execution from untrusted sources. All code must go through the standard parse-validate-compile pipeline. The bytecode loader validates the magic header (BRAID), version, and chunk boundaries before execution.
ARC Cycle Detection
The next and marked fields in the Object header support a cycle collector. When ARC alone cannot free circular references (e.g., parent-child relationships), the cycle detector traverses the object graph, identifies cycles, and breaks them to allow deallocation.