← All notes

Two KVM escapes, one precondition: the machine is still the boundary

Januscape and Zapscape are both guest-to-host escapes in KVM's shadow MMU, disclosed months apart in 2026. Both need the same thing from the attacker: a guest running virtual machines of its own. A container escape needs no such setup, and an AppVM never runs nested VMs. That gap is the argument.

Picture a workload that has turned hostile. Maybe it was compromised in the night, maybe it was untrusted from the first second: an agent running commands you did not write, or a build step chewing on someone else’s code. One question decides how bad the morning is. What stands between it and the rest of the machine?

For a container, one wall, and a wall it was already sharing. For an AppVM, its own machine. Twice this year, KVM gave us a clean way to see why the difference matters.

A container escape lands on a kernel you shared

A container is a process with a narrow view of its own world. Namespaces hide the host, cgroups cap what it can use, and seccomp trims the calls it can make. All of it is enforced by one kernel, the host’s, and every container on the box shares that kernel.

So a container escape is usually a single kernel bug reachable from inside: a bad syscall, a namespace edge case, a driver left in reach. Find one, and you are not in a neighbor’s container. You are on the host, beside every other workload on it. Nothing else has to be true first. The kernel you attack is already there, shared, on the very first call.

An AppVM gives each workload its own kernel

AppVM starts from the other side. You hand it a container image, and it boots that image as its own virtual machine on Linux and KVM, with its own kernel and a boundary the hardware enforces. No container engine runs in the guest. Nothing is a slice of a shared kernel, because there is no shared kernel.

That moves the wall. To reach the host, an attacker has to break out of their own guest kernel first, and then defeat the hypervisor under it. The step that counts, the hypervisor, is a far smaller and more deliberate target than a whole host kernel, and it was built to be exactly that.

Container

One wall, then a kernel everyone shares

  1. Compromised workload
  2. one shared wall · namespaces / cgroups / seccomp
  3. Host kernel shared by every container
  4. The host
Same attacker, two paths. The container shares one wall and the kernel behind it. The AppVM makes them break their own kernel, then a hardware wall, before the host is even in reach.

The hypervisor has bugs too. Here are two.

That is the honest objection: the second wall is not magic, and hypervisors have bugs. In 2026, KVM shipped two of them from the same corner of the code, the shadow MMU that stands in for a guest’s page tables.

2026 · KVM shadow-MMU guest-to-host escapes

Aspect JanuscapeCVE-2026-53359 ZapscapeCVE-2026-64561
Flaw Use-after-free in the shadow MMU Use-after-free in the shadow MMU
Reaches Intel and AMD AMD, or Intel from Ice Lake-SP on
Seen doing Reliable host crash; escape researched Guest-to-host escape
Fixed upstream June 2026 July 2026
Needs Guest root + nested virtualization Guest root + nested virtualization
Two bugs, months apart, same corner of KVM. The last row is identical.

Januscape had been reachable for about sixteen years and fires on both Intel and AMD. Zapscape is narrower, wanting AMD or a recent Intel part. Read the details however you like. The row that matters is the last one, and it reads the same in both columns.

What both escapes needed

Both needed the guest to be running virtual machines of its own. Nested virtualization is the precondition, and without it neither bug is reachable. That is not a footnote. It is the whole path: the shadow MMU only does this work when a guest builds a second layer of page tables underneath it.

An AppVM runs one application. It is not a hypervisor, and it starts no machines of its own, so it never walks the path these bugs live on. A container has no such gate. It asked for nothing and set up nothing, and its target kernel was shared from the start.

So the two models fail in different shapes. The shared kernel is escaped by one reachable bug and no setup at all. The machine boundary, in a bad year, was escaped only by a specialized flaw plus a guest that had gone out of its way to run VMs.

The point is the odds, not a guarantee

None of this says a virtual machine can never be escaped. Januscape and Zapscape prove it can. The claim is narrower and more useful. Giving each workload its own machine turns host takeover from the cheap, common result of one kernel bug into a rare and gated one, and this year the gate was a condition ordinary app workloads never meet.

The fixes make the last point on their own. Each was a kernel update, and on Virtainer the host is one immutable image, so a patch arrives as a new host image you pull and boot, with a clean rollback in a single reboot if anything misbehaves. Two escapes, two updates, the same quiet reboot. The machine is the boundary, and the boundary is the product.

More on why the machine is the unit in The middle ground has two doors. Learn about Virtainer at virtainer.io, or get in touch.

KEEP READING
After Apple, Docker agrees that AI agents belong in their own machine Aug 16 Run DeepSeek Harness as an AppVM, step by step Aug 15