Skip to content
NorscodeNorscode

Inside the runtime: memory, security and why your program cannot lie

ExampleBy the Norscode project

A walkthrough of how the Norscode runtime handles memory, why the capability model cannot be bypassed, and what it means for code you have not read.

Most languages do not let you see what happens underneath. This article goes through how the Norscode runtime actually works — because it explains why some things are safe here that are not safe elsewhere.

The arena

All allocation goes through an arena with 8-byte alignment. The arena has an upper limit, reuses freed blocks by first-fit, and has protection against double freeing. If memory runs out, you get a clean error — not undefined behavior.

The important thing is that the arena is movable. During compaction, live objects are physically moved together, and a relocation table updates all the addresses that pointed to them. Fragmentation therefore does not become a slow leak the way it often does in long-running services.

The collection

The garbage collector is mark/sweep with active frame scanning: the runtime walks through the call stack's frames to find the roots, instead of trusting that the programmer has kept order.

It handles cycles. Two objects that point to each other without anything else pointing to them are collected — something pure reference counting cannot do without help.

Byte accounting is kept, and collection is threshold-driven: small, frequent collections of young objects, rarer full rounds. Objects that survive enough rounds are promoted from young to old. This follows the common observation that most objects die young, and that those that survive the first rounds usually live long.

When code cannot be moved

A detail that says a lot about how carefully this is done: regions held by the compiler's native part with raw C pointers are pinned. They are not moved during compaction, because a raw pointer cannot be updated by the relocation table. Moving them would give a pointer that silently pointed wrong — the worst class of error.

This is the kind of detail you only get right if you own the whole stack.

Why capabilities cannot be bypassed

Here is the point that matters most in practice.

In most languages a program inherits the rights of the user who started it. A dependency five layers down the tree can read your key files, write anywhere and call out onto the network — and you do not discover it without scrutinizing every line.

In Norscode access is given when the program is started:

NORSCODE_VM_CAPABILITIES="disk.read" \\
NORSCODE_VM_DISK_ROOT="/srv/data" \\
  nc run rapport.no

The decisive thing is that there is no API for requesting more along the way. The capabilities are not a setting the program reads and can choose to ignore — they are the boundaries the runtime enforces around every single operation. A call that goes outside stops with manglar capability.

That means a library you have not read cannot reach further than you have let it. Not because it behaves nicely, but because the path does not exist.

What this gives you

Three things that are hard to achieve separately, and that hang together here:

  1. Memory is safe without you managing it. No manual freeing, no use-after-free, no double free.
  2. Long-running services do not fragment. Compaction moves live objects together instead of letting the holes grow.
  3. Access is something you give, not something assumed. That makes it defensible to run code you have not scrutinized in full.

Where you find it

All of this is written in Norscode and can be read: memory management in std.runtime_memory and std.runtime.allocator, the call stack in std.runtime_stack, the type system at runtime in std.runtime_type, exception handling in std.runtime_exception, the security layer in std.runtime_security, the threads in std.sched and std.tråd, and a baseline JIT in std.runtime_jit.

You do not need to learn another language to understand how your code is run. That is one of the underlying reasons the language is self-hosting: the toolchain is readable by anyone who knows the language it compiles.

Read more about the language · See the standard library

Related

Back to the overview