Summary
This is a longstanding issue on my dev box. After a while my available memory shrinks to the point where I need to reboot. Closing all applications doesn't help. Even logging out doesn't help, only a full reboot. The memory is essentially leaked in the kernel (161k accumulated handles), see below for details. For a long time I wasn't aware where it comes from, but it looks like I managed to find a reproducer. This is not directly "the sandbox" bug, but it's the closest I can get to reasonable place to report this issue.
Every Windows Sandbox session causes the host's System process to accumulate kernel file handles to the host OS files backing the sandbox image (\Windows\WinSxS\...). The handles are never released, not when the sandbox is closed, not when its VM is killed. Only a host reboot recovers them. One session with moderate in-guest file activity leaks ~30,000 handles and ~0.6 GB of host kernel pool.
The same mechanism leaks on every Windows 11 machine even without Sandbox. The always-running CmService zygote container (vmmemCmZygote) accumulates handles the same way. On my dev box this reached 161k handles and ~15 GB of kernel pool after 19 days of uptime, invisible in Task Manager (details at the end).
Environment
Windows 11 Pro 25H2 (26100.1.amd64fre.ge_release.240331-1435), 64 GB RAM (I know, not enough, but in this economy?), AMD Ryzen 9 7950X in case it depends on the hypervisor.
Reproduction
- Count System-process file handles matching the leak signature (GrantedAccess
0x00120089):
(PS because it was easy to script this... for quick check you can look at all handles for System (Get-Process -Id 4).HandleCount without filtering)
Add-Type @'
using System; using System.Runtime.InteropServices;
public class HC {
[DllImport("ntdll.dll")]
static extern int NtQuerySystemInformation(int c, IntPtr b, int l, out int r);
public static long Count() {
int len = 64*1024*1024; IntPtr buf = Marshal.AllocHGlobal(len); int ret;
if (NtQuerySystemInformation(64, buf, len, out ret) != 0) return -1;
long n = Marshal.ReadInt64(buf), c = 0;
for (long i = 0; i < n; i++) {
IntPtr e = new IntPtr(buf.ToInt64() + 16 + i*40);
if (Marshal.ReadIntPtr(e,8).ToInt64() != 4) continue; // System
if ((uint)Marshal.ReadInt32(e,24) == 0x00120089) c++; // FILE_GENERIC_READ
}
Marshal.FreeHGlobal(buf); return c;
}
}
'@
[HC]::Count()
- Save as
repro.wsb and run it. The logon command makes the guest read the first bytes of 20,000 files under C:\Windows\WinSxS, which are host-backed and served over vSMB:
<Configuration>
<LogonCommand>
<Command>powershell -ExecutionPolicy Bypass -Command "Get-ChildItem C:\Windows\WinSxS -Recurse -File -ErrorAction SilentlyContinue | Select-Object -First 20000 | ForEach-Object { try { $s=[IO.File]::OpenRead($_.FullName); $b=New-Object byte[] 16; $null=$s.Read($b,0,16); $s.Close() } catch {} }"</Command>
</LogonCommand>
</Configuration>
- Re-run the counter (from step 1) on the host, then stop the sandbox (
wsb list / wsb stop --id <id>), wait a few minutes, count again.
Note: the leak is one handle per unique backing file, deduplicated against files already pinned. A second run reading the same files adds almost nothing. To see growth again in the same boot session, read different files (e.g. Select-Object -Skip 20000 -First 20000, which wsb exec can run in a live sandbox).
Measured results (two consecutive sandbox cycles, same boot)
| Step |
Handle count |
| Baseline (no sandbox) |
3,179 |
| Cycle 1: guest reads 20k WinSxS files (~3 min) |
33,093 |
Cycle 1: sandbox VM killed (hcsdiag kill) |
33,087 |
| Cycle 2: new sandbox, guest reads the same 20k files |
33,105 |
Cycle 2: guest reads the next 20k files (wsb exec) |
54,068 |
Cycle 2: graceful wsb stop |
54,052 |
Each unique file costs one permanent handle (+20,963 for 20k fresh files). Stopping and hard kill leak equally. The handles are permanent until host reboot. Host paged pool retained ~0.6 GB per ~30k handles (FltMgr name cache FMfn + File + NTFS metadata scale with each pinned FILE_OBJECT).
ETW fun
I verified the creation stacks with ETW twice (WPR Handle profile): a boot trace covering the CmZygote accumulation, and a trace of the Sandbox reproduction itself. In the traced repro run, 145,402 new handles were created. 100% matched to creation events in the trace, all in vmwp.exe context, with this creation stack (400/400 sampled):
storvsp!VspVsmbHandleRelativeCreateFileRequest ; vSMB file open for the guest
storvsp!VspVsmbCommonRelativeCreate
nt!IoCreateFileEx
wcifs!WcPostCreate
wcifs!WcProcessWciReparsePointOpen ; container-layer reparse point
wcifs!WcGetSourceList
wcifs!WcOpenSourceFile ; opens the backing host file
FLTMGR!FltCreateFileEx2
nt!ObpCreateHandle ; the leaked kernel handle
vSMB serves the guest's host-backed file, wcifs follows the container-layer reparse point and opens the backing file with a kernel handle that is never closed and survives VM teardown.
(Side note from the traced run: the guest OS's own background activity touched far more host-backed files than my scripted reads. That run leaked ~145k handles total, all through the same stack.)
Long-term impact
The CmZygote container leaks through the identical stack. After 18.9 days of uptime on this machine: 166,905 System handles (~161k matching the signature), pinning 4.1M FILE_OBJECTs and ~15 GB of kernel pool (FMfn 5.37 GB paged / 14.3 M allocations, File 1.56 GB nonpaged / 4.1 M, NTFS metadata ~1.9 GB), with system commit at 31 GB while all processes combined used ~10 GB. A full handle-table inventory from a live kernel dump resolved 137,678 distinct backing files, practically all under \Windows\WinSxS\.
Like I said, this is not specifically a Sandbox issue, but I'd love to get it fixed, and this is the easiest way to reach you guys. At least the "leak" is not duplicating handles per file, but I would expect them to be cleared when all VMs/containers are closed. I mean sure, you may cache some things in the kernel, but upwards of ~15 GB is a bit excessive. And I don't really know what the upper bound is, because at this point I'm always forced to reboot my dev box. 31 GB of memory commit without any running applications leaves it not very usable.
Also note that I'm not sure this is the only mechanism leaking these handles, but it's the one I'm able to reproduce quickly. Once fixed, I will be able to reconfirm over a longer uptime.
Questions
If you have questions or need more info, let me know. I have a kernel memory dump from the 19-day uptime case described above, so I can dig into it if you have specific requests. I also have a WPR boot trace which shows the "leaked" handles being created. But in general, I think you should be able to repro this easily with the Sandbox example.
Summary
This is a longstanding issue on my dev box. After a while my available memory shrinks to the point where I need to reboot. Closing all applications doesn't help. Even logging out doesn't help, only a full reboot. The memory is essentially leaked in the kernel (161k accumulated handles), see below for details. For a long time I wasn't aware where it comes from, but it looks like I managed to find a reproducer. This is not directly "the sandbox" bug, but it's the closest I can get to reasonable place to report this issue.
Every Windows Sandbox session causes the host's System process to accumulate kernel file handles to the host OS files backing the sandbox image (
\Windows\WinSxS\...). The handles are never released, not when the sandbox is closed, not when its VM is killed. Only a host reboot recovers them. One session with moderate in-guest file activity leaks ~30,000 handles and ~0.6 GB of host kernel pool.The same mechanism leaks on every Windows 11 machine even without Sandbox. The always-running
CmServicezygote container (vmmemCmZygote) accumulates handles the same way. On my dev box this reached 161k handles and ~15 GB of kernel pool after 19 days of uptime, invisible in Task Manager (details at the end).Environment
Windows 11 Pro 25H2 (26100.1.amd64fre.ge_release.240331-1435), 64 GB RAM (I know, not enough, but in this economy?), AMD Ryzen 9 7950X in case it depends on the hypervisor.
Reproduction
0x00120089):(PS because it was easy to script this... for quick check you can look at all handles for System
(Get-Process -Id 4).HandleCountwithout filtering)repro.wsband run it. The logon command makes the guest read the first bytes of 20,000 files underC:\Windows\WinSxS, which are host-backed and served over vSMB:wsb list/wsb stop --id <id>), wait a few minutes, count again.Note: the leak is one handle per unique backing file, deduplicated against files already pinned. A second run reading the same files adds almost nothing. To see growth again in the same boot session, read different files (e.g.
Select-Object -Skip 20000 -First 20000, whichwsb execcan run in a live sandbox).Measured results (two consecutive sandbox cycles, same boot)
hcsdiag kill)wsb exec)wsb stopEach unique file costs one permanent handle (+20,963 for 20k fresh files). Stopping and hard kill leak equally. The handles are permanent until host reboot. Host paged pool retained ~0.6 GB per ~30k handles (FltMgr name cache
FMfn+File+ NTFS metadata scale with each pinned FILE_OBJECT).ETW fun
I verified the creation stacks with ETW twice (WPR
Handleprofile): a boot trace covering the CmZygote accumulation, and a trace of the Sandbox reproduction itself. In the traced repro run, 145,402 new handles were created. 100% matched to creation events in the trace, all invmwp.execontext, with this creation stack (400/400 sampled):vSMB serves the guest's host-backed file,
wcifsfollows the container-layer reparse point and opens the backing file with a kernel handle that is never closed and survives VM teardown.(Side note from the traced run: the guest OS's own background activity touched far more host-backed files than my scripted reads. That run leaked ~145k handles total, all through the same stack.)
Long-term impact
The CmZygote container leaks through the identical stack. After 18.9 days of uptime on this machine: 166,905 System handles (~161k matching the signature), pinning 4.1M FILE_OBJECTs and ~15 GB of kernel pool (
FMfn5.37 GB paged / 14.3 M allocations,File1.56 GB nonpaged / 4.1 M, NTFS metadata ~1.9 GB), with system commit at 31 GB while all processes combined used ~10 GB. A full handle-table inventory from a live kernel dump resolved 137,678 distinct backing files, practically all under\Windows\WinSxS\.Like I said, this is not specifically a Sandbox issue, but I'd love to get it fixed, and this is the easiest way to reach you guys. At least the "leak" is not duplicating handles per file, but I would expect them to be cleared when all VMs/containers are closed. I mean sure, you may cache some things in the kernel, but upwards of ~15 GB is a bit excessive. And I don't really know what the upper bound is, because at this point I'm always forced to reboot my dev box. 31 GB of memory commit without any running applications leaves it not very usable.
Also note that I'm not sure this is the only mechanism leaking these handles, but it's the one I'm able to reproduce quickly. Once fixed, I will be able to reconfirm over a longer uptime.
Questions
If you have questions or need more info, let me know. I have a kernel memory dump from the 19-day uptime case described above, so I can dig into it if you have specific requests. I also have a WPR boot trace which shows the "leaked" handles being created. But in general, I think you should be able to repro this easily with the Sandbox example.