A guest asks the host to finish a network checksum. It supplies the offsets. The host writes into an IPv4 header it has already parsed, and ends up sending host-kernel memory out on the wire.
That's the bug described in PwnEscape, disclosed by Paulos Yibelo of pwn.ai this month. It lives in the Linux host kernel. A VMM that passes through the guest's checksum metadata can give it the input it needs.
hvi doesn't pass that metadata through. We strip the guest's virtio-net header and, on Linux tap, replace it with twelve zero bytes. The guest can put whatever it wants in its header. Those bytes never reach the host kernel.
There is an important distinction for Brig users: Brig runs Cloud Hypervisor on Linux, not hvi. Brig 0.4.0 on an unpatched Linux host is exposed. Brig 0.4.1, released today, upgrades that runtime and closes this path.
On a Mac there is no Linux host kernel, so this bug cannot reach the host whichever VMM runs the sandbox. Six of Brig's eight macOS profiles use hvi and the other two use Apple's Virtualization framework. hvi's header handling is what protects hvi on Linux.
Running Brig on Linux? Upgrade to 0.4.1 and patch your host kernel. On macOS, no action is needed for this bug.
The public writeup withholds the full exploit chain. This post sticks to the published bug, the upstream patch, and our source code.
How a checksum becomes a memory leak
A virtio-net header lets a guest ask the host to do networking work on its behalf. For checksum offload, the guest supplies three things: a flag requesting checksum completion, a csum_start, and a csum_offset.
When a VMM writes that header to a tap device opened with IFF_VNET_HDR, the host kernel reads it and marks the packet CHECKSUM_PARTIAL. The checksum gets completed later.
The problem is where “later” happens. The host has already parsed the IPv4 header on ingress and trusted its length. ip_do_fragment() then completes the checksum, using offsets supplied by the guest, and reads the header length again.
The writeup demonstrates changing the IPv4 header length from 20 to 60 bytes. The fragmenter then copies 60 bytes of header from a frame holding only 28 bytes of data. The extra 32 bytes come from host-kernel memory and leave on the wire as IP options.
The upstream fix by Michael S. Tsirkin, ipv4: validate checksum_start before completing checksum, rejects a checksum start inside the IPv4 header. It adds the check before checksum completion at five sites, including the IPv4 fragmenter. It reached mainline on 9 October in the net-7.3-rc7 merge (af32da41b032). The stable releases followed on 15 October, tracked as CVE-2026-99999. The CVE record lists the fixed version for each stable branch, so check your distribution's kernel against it.
The writeup describes that patch as defense in depth and leaves validation at virtio_net_hdr_to_skb(), where the metadata enters the kernel, as an open question.
What the VMM passes through matters
QEMU, Firecracker, and Cloud Hypervisor have paths that forward the guest's virtio-net header to the tap. Offloads are useful: they let the guest hand checksum and segmentation work to the host and improve networking throughput. The kernel is responsible for validating the metadata it consumes. Here, that validation was missing.
Cloud Hypervisor v54.0 turns offloads off by default (#9041). It also includes #9025: while checksum offload is off, the device drops transmit chains whose header asks for a checksum or segmentation offload. With offload_csum=on, the guest's checksum offsets reach the tap again.
Both parts matter. A hostile guest driver can ignore the advertised features and set NEEDS_CSUM anyway. The monitor has to enforce the restriction before forwarding the header.
Why hvi isn't affected
hvi has three networking backends: a user-space stub stack, a relay to a gvisor-tap-vsock gateway, and a Linux tap. Only the tap backend hands frames to the host kernel. Three things prevent the guest's offload metadata from reaching it:
- We offer no offloads. The device advertises only
VIRTIO_F_VERSION_1andVIRTIO_NET_F_MAC. No checksum offload, no segmentation offload. Feature set. - We discard the guest's header.
read_tx_framestrips it from every transmit chain and returns the Ethernet frame. It does this regardless of what the guest wrote. Transmit path. - We write our own tap header.
prepend_vnet_hdrprefixes twelve zero bytes. No flags, no GSO type, no checksum offsets. Tap header.
The resulting tap frame doesn't ask the kernel to complete a checksum. The guest-controlled offsets that trigger this bug never get there. This holds even when a malicious driver requests features we never offered.
We didn't design this around PwnEscape. hvi started on macOS, where its backends consume bare Ethernet frames. We stripped the header at the device queue, before choosing a backend. The Linux tap came later and needed a virtio-net header of its own; with no offloads negotiated, an all-zero header was the correct one.
That also fits how Brig observes guest traffic: at the device, as Ethernet frames, without segmentation super-frames. The security benefit came from those design choices. Now it is an explicit requirement.
An undocumented property is easy to lose
Someone will eventually want more throughput. Adding VIRTIO_NET_F_CSUM and forwarding the guest's header could look like a straightforward performance improvement. It would also change this security boundary.
PR #294 adds a regression test and documents the requirement.
The test builds a transmit chain with NEEDS_CSUM, TCPv4 GSO, and checksum offsets pointing into the IPv4 header. It sends that chain through the device's MMIO transmit path in tap mode and checks what the tap receives:
- Twelve zero bytes of virtio-net header.
- The original Ethernet frame, unchanged.
- No extra data.
Changing the generated header from zeros to ones makes the test fail. The behavior is also documented in networking.md and in the boundary table in security.md.
Future offload support needs to account for that requirement. The test makes an accidental change visible.
What changes for Brig on Linux
Brig 0.4.0's Linux bundle uses Cloud Hypervisor v50.0 through urunc. That version offers offloads by default and forwards the guest's header. Even setting offload_csum=off doesn't prevent a hostile driver from supplying the metadata. An unpatched host is exposed.
Brig 0.4.1 moves to Linux bundle v0.1.0-rc17, with Cloud Hypervisor v54.0. Brig leaves offloads at that version's default, which is off, so the device drops a header that asks for checksum completion. The bundle also updates urunc to specify the disk image type v54 requires, so sandboxes with a block-device rootfs keep booting.
Upgrade to Brig 0.4.1. Patch the host kernel too. The runtime update stops this input from a Brig sandbox; the kernel fix also protects affected paths used by other VMMs on the same host.
If you remain on Brig 0.4.0, the host kernel patch is the mitigation. Until it is installed, account for the exposure when deciding what runs on that host.
hvi also runs on Linux under KVM and doesn't expose this path there. It is not Brig's Linux default yet. We're fixing and hardening its virtio devices before making that switch.
The cost, and the limit of the claim
Without offloads, the guest computes its own checksums and sends packets no larger than the MTU. That costs CPU and gives up throughput compared with a VMM that negotiates TSO. For agent sandboxes, we're comfortable with that trade.
hvi still has a host-facing attack surface: KVM, device emulation, and other guest-to-host paths. ITScape is another example on arm64. We use seccomp on Linux and Seatbelt on macOS because we expect more bugs.
The claim here is specific: hvi doesn't give this bug its input, and we have a regression test for the behavior that prevents it.
If you're evaluating agent sandboxes, ask what crosses the boundary and which assumptions have tests behind them. Our code and test are public.
Run it yourself
Brig is open source. Start a sandbox and check the boundary from inside it:
# macOS
brew install --cask brig
# Linux, x86-64 or arm64: installs Brig 0.4.1 with the runtime bundle
curl -fsSL https://raw.githubusercontent.com/brig-sh/brig/main/install.sh | sh
# either host
brig run ubuntu ~/code/demo uname -r # the guest kernel, not yours; no account needed
brig run claude ~/code/demo # Claude Code in the same kind of sandboxbrig.sh has the full install notes and pairs each security claim with the command that tests it. If something in this post looks wrong, the code is public: open an issue.


