How to compile and run C code in a sandbox
Write the source into a Linux microVM, compile it with gcc there, and run the binary with a time limit and the internet off.
On Runtime there is nothing to install: gcc and make are in every sandbox. Each sandbox is a Firecracker microVM running Ubuntu 24.04.5 with build-essential, so a C file compiles in the first command after create. A new sandbox took 351 ms from the create request to its first Python result at the median, measured on 24 September 2026 (speed), which makes a fresh machine per program practical.
Why C needs a real boundary
C code can overflow a buffer, write through a stray pointer, fork until the process table is full or spin forever. In a container all of that runs on the host's kernel. In a Runtime sandbox it runs on the microVM's own kernel, with CPU, memory, network and cost enforced on the host, outside the VM (security). The worst a bad program can do is break a machine you were going to throw away.
What is installed and what to add
| Tool | In the default image | Ubuntu 24.04 package, checked 25 September 2026 | Add it with |
|---|---|---|---|
| gcc, make | Yes (build-essential) | gcc-13 13.3.0, build-essential 12.10 | nothing |
| clang | No | clang-18 18.1.3 | sudo apt-get install -y clang |
| CMake | No | cmake 3.28.3 | sudo apt-get install -y cmake |
| gdb | No | gdb 15.0.50 | sudo apt-get install -y gdb |
| Valgrind | No | valgrind 3.22.0 | sudo apt-get install -y valgrind |
| SQLite headers | No | libsqlite3-dev 3.45.1 | sudo apt-get install -y libsqlite3-dev |
sudo works without a password inside the sandbox
(the sandbox environment).
Compile and run a program
Write the file, compile it, and run the binary in two separate commands, so a compiler error and a crash come back as different results.
TypeScriptimport { Sandbox } from "withruntime";const source = `#include <stdio.h>int main(void) { long total = 0; for (int i = 1; i <= 100; i++) total += i; printf("%ld\\n", total); return 0;}`;await using sbx = await Sandbox.create({ network: { internet: false }, timeoutSeconds: 300, onLeaseEnd: "stop",});await sbx.files.write("/workspace/main.c", source);const build = await sbx.exec(["gcc", "-O2", "-Wall", "-Wextra", "-o", "main", "main.c"]);if (build.exitCode !== 0) throw new Error(build.stderr);const run = await sbx.exec(["./main"], { timeoutMs: 10_000 });console.log(run.exitCode, run.timedOut, run.stdout); // 0 false 5050Pythonfrom withruntime import Sandboxsource = """#include <stdio.h>int main(void) { long total = 0; for (int i = 1; i <= 100; i++) total += i; printf("%ld\\n", total); return 0;}"""with Sandbox.create(network={"internet": False}, timeout_seconds=300, on_lease_end="stop") as sbx: sbx.files.write("/workspace/main.c", source) build = sbx.exec(["gcc", "-O2", "-Wall", "-Wextra", "-o", "main", "main.c"]) if build.exit_code != 0: raise SystemExit(build.stderr) run = sbx.exec(["./main"], timeout_ms=10_000) print(run.exit_code, run.timed_out, run.stdout)- An array runs the program directly, with no shell, so file names and arguments from a model cannot inject commands (run commands).
- A program that never ends returns
timedOut: truewith its output so far. The default limit is 60 seconds. - A program that prints forever is capped: a result keeps at most 64 KiB of stdout and 64 KiB of stderr, and says what it dropped.
- gcc's messages go to stderr. Send them back to the model as they are; it can fix its own code from them.
Catch memory bugs with sanitizers
GCC's AddressSanitizer detects out-of-bounds and use-after-free bugs, and UndefinedBehaviorSanitizer detects undefined behaviour at run time, both from compiler flags (GCC instrumentation options). Nothing extra is installed:
TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create({ network: { internet: false } });await sbx.files.write( "/workspace/bug.c", "#include <stdlib.h>\nint main(void) { int *a = malloc(4 * sizeof *a); a[4] = 1; free(a); return 0; }\n",);await sbx.exec(["gcc", "-g", "-fsanitize=address,undefined", "-o", "bug", "bug.c"], { check: true,});const run = await sbx.exec(["./bug"], { timeoutMs: 30_000 });console.log(run.exitCode, run.stderr.slice(0, 2000)); // the sanitizer's reportFor Valgrind or gdb, install them once per sandbox, or bake them into an image as below.
Build a Makefile project
Upload a folder and run make in it. The upload travels as one archive and
keeps file modes, so scripts stay executable.
Pythonfrom withruntime import Sandboxwith Sandbox.create(network={"internet": False}) as sbx: sbx.files.upload("./project", "/workspace/project") build = sbx.exec(["make", "-j2"], cwd="/workspace/project", timeout_ms=300_000) tests = sbx.exec(["make", "test"], cwd="/workspace/project", timeout_ms=120_000) print(build.exit_code, tests.exit_code, tests.stdout[-2000:])Start every sandbox with your toolchain
A custom image installs the extra tools once. Building it is free; a stored image is charged on its size.
TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();await runtime.images.build({ name: "c-tools", recipe: { apt: ["clang", "cmake", "gdb", "valgrind"] },});await using sbx = await runtime.sandboxes.create({ image: "c-tools", network: { internet: false },});console.log((await sbx.exec(["valgrind", "--version"])).stdout);Terminalruntime image build --apt clang,cmake,gdb,valgrind --name c-toolsWhat it costs
Runtime bills the CPU a sandbox uses, at $0.025 per vCPU-hour, and memory at $0.0075 per GiB-hour. A compile keeps a core busy for a moment; waiting costs the floor of a twentieth of a vCPU. A 2 vCPU, 4 GiB sandbox costs $0.03125 an hour idle and $0.08 an hour with both cores busy (pricing). New accounts get 50 free sandbox hours, no card:
Terminalnpx withruntime sandbox run --trial -- gcc --versionFor C++, see how to run C++ code in a sandbox. To grade many submissions, see grading student code, and for code a model wrote, running untrusted LLM code.
Sources
Checked 25 September 2026.
- Ubuntu 24.04 (noble) packages: gcc-13, build-essential, clang-18, cmake, gdb, valgrind, libsqlite3-dev
- GCC instrumentation options
Facts on this page were checked on 25 September 2026.