Cybersecurity for AI Systems from First PrinciplesPost 2 of 19 · 19 of 19 written

2. How computers fail

Cybersecurity · 2 of 195 min readOct 2026

To a computer, instructions and data are both just bytes; the difference exists only by convention. Most low-level attacks happen when an attacker makes the system cross that convention - data gets executed, or reads and writes go where they shouldn’t.

The root cause: code and data share one medium

In the von Neumann design, programs and data live in the same memory. Above the hardware, the same pattern repeats: software is full of interpreters. A SQL engine, a shell, an HTML renderer, a template engine, a YAML loader, Python’s pickle, and an LLM are all interpreters. Each one treats some of its input as commands.

Injection is the general name for attacker-controlled data reaching an interpreter in a position where it is treated as code. The general cure is structural: keep code and data in separate channels. Parameterized SQL queries do this - the query shape is sent separately, and user values can never become SQL syntax. Escaping strings is a weaker, error-prone substitute.

Hold on to this idea. Part 10 shows that LLMs have no separate channel for instructions and data, which is why prompt injection is so hard.

Memory safety

A running program keeps its memory in regions. The stack holds function calls, local variables, and return addresses (where to continue after a function ends). The heap holds data allocated while the program runs.

In C and C++, the language does not check that a write stays inside the buffer it targets. A buffer overflow writes past the end of a buffer and overwrites whatever sits next to it. If that neighbour is a return address or a function pointer, an attacker can redirect the program’s execution. Related bug classes:

  • Out-of-bounds read - reading past a buffer. Heartbleed (2014) let anyone read chunks of server memory, including private keys.
  • Use-after-free - memory is freed, then used again after the attacker has placed their own data there.
  • Integer overflow - a size calculation wraps around, so a buffer is allocated too small.

Operating systems add mitigations: ASLR randomizes where things sit in memory, non-executable memory stops data regions from running as code, and stack canaries detect overwritten return addresses. These raise attacker cost; they do not remove the bug. The real fix is memory-safe languages (Rust, Go, Java, Python). Microsoft and the Chromium project have both reported that roughly 70% of their serious security bugs were memory-safety bugs (approximate figures, from public statements around 2019–2020).

AI link. Your Python is memory safe; the ML stack under it is not. PyTorch internals, CUDA kernels, tokenizers, image and video decoders, and C++ inference servers all parse input in native code. When an untrusted image, audio file or model file enters your pipeline, it enters C and C++ parsers.

Processes, privilege and isolation

The operating system kernel controls hardware and memory. Programs run in user mode and must ask the kernel for anything privileged through system calls; that interface is a major trust boundary. Each process gets its own virtual memory, so one process cannot normally read another’s. Each runs as a user with permissions; root (or Administrator) can do anything.

Privilege escalation means gaining more rights than granted. Vertical escalation moves to a higher privilege level (user to root). Horizontal escalation moves sideways to another account at the same level (your account to someone else’s).

Isolation comes in strengths. Pick the strength that matches how untrusted the code is:

Boundary What separates workloads Main weakness
Process Separate virtual memory, user permissions Same kernel, same file system, often same network
Container Kernel namespaces and cgroups Shares the host kernel; a kernel bug can escape
Sandboxed runtime (e.g. gVisor) An extra layer intercepting system calls Smaller kernel attack surface, some overhead
Virtual machine / microVM (e.g. Firecracker) Hardware virtualization, separate kernel Hypervisor bugs are rare but possible
Separate machine Physical separation Cost; network paths between them still matter

AI link. If an LLM can run code (a “code interpreter” tool), that code is untrusted, no matter how it was written. Run it in a microVM or gVisor-class sandbox, with no network by default, CPU, memory and time limits, and no access to secrets. Never run it in the same container that holds your API keys.

Parsing: the attack surface nobody draws

Every file-format parser is a small interpreter for attacker-controlled input. Classic failures:

  • XML external entities (XXE) - an XML file instructs the parser to read local files or make network requests.
  • Path traversal / Zip Slip - an archive entry named ../../etc/... writes outside the target folder.
  • Decompression bombs - a small file expands to gigabytes and exhausts memory.
  • Unsafe deserialization - formats that can describe arbitrary objects (YAML’s unsafe loaders, Java serialization, Python pickle) can run code while loading. For pickle this is by design, not a bug.

The discipline known as LangSec treats input as a formal language: define precisely what valid input looks like, parse it completely and strictly at the boundary, and only then act on it (“parse, don’t validate”). Two practical rules follow. Prefer allowlists (accept known-good) over blocklists (reject known-bad), because attackers find what you forgot to block. And canonicalize before checking: decode URLs, resolve paths and normalize Unicode first, or encoded tricks slip past your check.

AI link. RAG ingestion parses PDFs, HTML and Office files from untrusted sources. Model loading parses weight files. Data pipelines parse scraped content. Each is a parser exposed to attackers.

Time and races

TOCTOU (time-of-check to time-of-use): the system checks something, then uses it later, and the attacker changes it in between. A file is checked as safe, then replaced before it is opened. In AI systems: an agent approves a URL or a tool definition, and its content changes before or between uses (Part 11 calls this a “rug pull”).

Questions to ask

  • Where does untrusted data reach an interpreter: SQL, shell, template, deserializer, or model?
  • Are code and data separated structurally, or only by escaping strings?
  • Which native-code parsers touch untrusted input in this pipeline?
  • What privileges does this process run with, and what could an attacker reach from it?
  • What isolation boundary separates untrusted code from secrets and the network?
  • Is anything checked at one time and used at another?