Commit Graph
87 Commits
Author SHA1 Message Date
James Stringer 9f1772ce60 Add MIT LICENSE and third-party license notices
- LICENSE: MIT, Copyright (c) 2026 James Stringer.
- THIRD-PARTY-NOTICES.md: scopes the MIT grant around bundled third-party
  components, documenting each one's license/copyright:
    dxgkrnl (Microsoft WSL2 GPU driver) GPL-2.0; iso-patch/engine/xz 0BSD;
    wsl-mesa prebuilt (Mesa 25.3.6) MIT; WDF SDK, WebView2 SDK, DevCon (Microsoft).
- tools/linux/dxgkrnl/COPYING: verbatim GPL-2.0 text shipped with the GPL'd
  driver source.
- tools/linux/wsl-mesa/prebuilt/MESA-LICENSE.txt: Mesa's headline MIT license,
  shipped next to the prebuilt binary.
- README: license section points to LICENSE + THIRD-PARTY-NOTICES.
- Info.plist: copyright string now names the holder and notes MIT.

The asb_drm Linux kernel module keeps its existing dual (GPL-2.0 OR MIT)
licensing; vendored sources keep their own SPDX headers.
2026-06-03 21:47:02 -04:00
James Stringer 31385be53e Fix macOS host backend bugs (clipboard, SSH proxy, agent, install, menu)
- H3  vm_clipboard_mac.m: FORMAT_LIST_V2 `ulen + 8 + 4` 32-bit overflow
      bypassed the bounds check (guest->host ~4GB OOB read); cast to size_t.
- H18/H19/H20 vm_ssh_proxy_mac.m: relay teardown detached-not-joined (UAF),
      double-closed fds, double-detached. Redesigned: relay is the sole fd
      closer (under the owner lock), -stop signals+shutdowns+joins, threads
      stay joinable and are reaped on stop / slot reuse.
- M4  vm_{agent,clipboard,ssh_proxy}_mac.m: vsock fd leaked when the VZ
      connect completion fired after the wait timed out; claim ownership via
      a flag + lock and close the late dup (corrected the timeout test to !=0).
- M1/M2 asb_core_mac.m: roll back the phantom VM when its dir can't be
      created; wipe the admin password on the agent-resources-missing path.
- M3  iso_patch_mac.m: auth keep-alive timer could use g_auth after
      releaseAuthorization freed it; free from a dispatch cancel handler.
- M5  AppDelegate.m: app has no nib, so [NSApp mainMenu] was nil and the
      View -> Event Log / Cmd+L item never installed; build a main menu.
- M19 vm_clipboard_mac.m (host half): preserve symlinks in clipboard file
      copy (new wire entry type) instead of following them -- a symlink to a
      directory made open() succeed but read() return EISDIR, aborting the
      tree mid-stream and hanging the guest. Found via live VM testing.
2026-06-03 20:58:30 -04:00
James Stringer 14daec375e Flush queued WebView2 messages once the UI is ready (L1)
ui.c: on the uiReady action, call webview2_flush_queue() so messages
posted before the JS message listener registered (e.g. early ui_log
lines) are delivered instead of silently dropped.
2026-06-02 21:50:51 -04:00
James Stringer b9629afe7f Fix memory-safety, leak, and concurrency bugs in Windows host backend
Bounds / overflow:
- H1/H5 vm_display_idd.c, asb_display.c: bound full-frame and dirty-rect
  copies by the bytes actually received (OOB read on malformed frame).
- H2 vm_display_idd.c: validate cursor shape_data_size vs pitch*height.
- H4/C9 vm_clipboard.c: reject short/oversized FORMAT_LIST frames and a
  name_len that runs past the buffer.
- H22 vm_clipboard.c: DROPFILES wcscpy_s passed MAX_PATH as destsz on a
  tightly-sized buffer, so the secure-CRT tail-fill overran the heap.

Use-after-free / double-close:
- H13 asb_core.c: resolve the VM by stable unique_id under g_cs in the
  build progress/completion paths (g_vms[] compaction left vm_index
  stale); free the orphaned HCS handle if the VM was deleted mid-build.
- H14 vm_agent.c, H21 vm_clipboard.c: claim sockets atomically with
  InterlockedExchangePointer so worker threads and the stop path never
  double-close the same (possibly recycled) handle.
- H15/H16 vm_ssh_proxy.c: shutdown() relay sockets from the stop thread
  instead of closing them, and wait INFINITE (cleanup is bounded) so the
  listener can't run inside a freed proxy.

Leaks / correctness:
- M6/M7 hcs_vm.c: free the HCS result document on failed queries.
- M8 disk_util.c: propagate IStream::Read and WriteFile failures.
- M9/L7 snapshot.c: read/write tree.dat as UTF-8; zero-init the GUID and
  check CoCreateGuid.
- H12 asb_core.c: fix the inverted wait condition in asb_vm_wait_running.
- H17 vm_display_idd.c: on WSAEWOULDBLOCK / partial input send, drop the
  packet or flag reconnect instead of misaligning the stream.
- L3 asb_core.c: bounds-check the snapshot index in the delete-branch log.
- L4/L5 gpu_enum.c: NUL-terminate InfPath; only CoUninitialize when COM
  was actually initialized.
- L6 hcn_network.c: skip entries with OnLinkPrefixLength > 32.
- L8 vmms_cert.c: fall back to "unknown" when GetComputerNameW fails.
2026-06-02 21:50:39 -04:00
James Stringer 1d0bab6b01 Stamp macOS version from Directory.Build.props at build time
Make Directory.Build.props the single place a version is ever set on macOS,
matching how Windows derives its version from the same file.

- Add a 'Stamp version' Run Script build phase to the AppSandbox and
  AppSandboxCore targets. It runs on every build (Xcode GUI, xcodebuild,
  Archive) and writes CFBundleShortVersionString / CFBundleVersion into the
  built bundle's Info.plist from Directory.Build.props.
- A build phase (not an xcconfig) is used because xcconfig is read before build
  phases run, so a generated xcconfig couldn't version the current build.
- tools/sign/asb-version.sh reads the version; tools/sign/stamp-version.sh is
  the build-phase wrapper. make-release-mac.sh uses asb-version.sh for the zip
  name.
- Remove the committed, generated version.xcconfig and sync-mac-version.sh;
  Local.xcconfig no longer includes version.xcconfig.

A fresh clone builds (ad-hoc or Personal Team, Xcode GUI or CLI) with the
version stamped, and nothing but Directory.Build.props defines it.
2026-06-01 21:42:11 -04:00
James Stringer a518eea507 Merge remote-tracking branch 'origin/main' into mac-build 2026-06-01 21:06:41 -04:00
James Stringer e2c578e228 Add macOS Developer ID signing, notarization, and release tooling
- xcconfig-based signing: Local.xcconfig (committed) pulls in a git-ignored
  LocalUser.xcconfig for the maintainer's team/Developer ID identity, so the
  public project builds ad-hoc/Personal-Team with no secrets committed.
- Release config in project.pbxproj: Developer ID + hardened runtime +
  secure timestamp + no get-task-allow; arm64; macOS 26 deployment target
  (guest helpers stay 13.0); scheme defaults to Release.
- Version derives from Directory.Build.props via sync-mac-version.sh ->
  version.xcconfig (the same single source of truth Windows uses).
- make-release-mac.sh: build -> verify-sign -> notarize (recursive, covers
  the framework + iso-patch-mac + appsandbox-agent + appsandbox-clipboard) ->
  staple -> Gatekeeper check -> publish a zip-only release_package/.
- Config split mirrors the Windows convention: mac-release-config.json
  (committed, no secrets) + mac-signing.local.json (git-ignored creds).
- tools/sign/SIGNING_MAC.md documents the whole process end to end.
2026-06-01 20:59:29 -04:00
James Stringer 994c5a36c2 Enable nested virtualization + WSL2 memory defaults for HCS VMs
Add ExposeVirtualizationExtensions to the Processor block, gated on host capability via HcsGetServiceProperties (ProcessorCapabilities -> "NestedVirt"), mirroring WSL2. Emitted only when the host advertises nesting support so HcsCreateComputeSystem never fails on ARM64 / older CPUs.

Also adopt WSL2's memory defaults: EnableDeferredCommit and EnableColdDiscardHint alongside the existing AllowOvercommit.
2026-06-01 19:05:36 -04:00
James Stringer 957bffd386 IDD display: focus an already-open window + add "Show Log" menu item
- Connecting to a VM whose IDD display is already open now brings that
  window to the foreground (via a thread-marshaled WM_IDD_FOCUS) instead
  of just logging "already open".
- The per-display log window is created hidden; a "Show Log" item on the
  window's system menu reveals it on demand (closing it just hides it).
2026-05-31 22:21:09 -04:00
James Stringer f4307c0e43 Add Windows guest OpenGL/OpenCL/Vulkan via D3D mapping layers
Provision the Microsoft D3D mapping layers (OpenGLOn12, OpenCLOn12, Dozen)
into Windows GPU-PV guests so apps get hardware-accelerated GL/CL/Vulkan on
top of the projected host D3D12 driver.

- d3dlayers.c/.h: fetch the D3DMappingLayers package from Microsoft on demand
  over WinHTTP (displaycatalog -> FE3 -> CDN), extract with in-box tar, cache
  beside the exe. Sets the Dozen ICD manifest library_path to the absolute
  guest path (the Vulkan loader's LoadLibraryEx fallback uses
  LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR, which requires a fully-qualified path).
- fe3_templates.h: SOAP envelopes for the FE3 client.
- gpu_enum.c/.h: add the AppSandbox.GlLayers Plan 9 share (guest path
  C:\Windows\AppSandbox\d3dlayers).
- asb_core.c: stage the layers + OpenGL shim and append the share before VM start.
- disk_util.c: skip the GlLayers share during build-time image staging.
- agent.c: gl_provision() copies dxil.dll to System32 and registers the ICDs
  (OpenGL via AppInit_DLLs shim, OpenCL + Vulkan via Khronos registry keys)
  after the GPU driver copy, before the device disable/enable cycle.
- tools/gl-shim: appsandbox_gl_shim.dll, an AppInit_DLLs shim that IAT-hooks
  gdi32!D3DKMTQueryAdapterInfo to route opengl32 to OpenGLOn12.dll.
- AppSandbox.sln / AppSandboxCore.vcxproj: build wiring.
2026-05-30 22:07:24 -04:00
James Stringer b24a45150d Pull guest source from main now that Linux support is merged
The prefetch-repo step baked the wip-linux-vm-support feature branch as
the ref it downloads the agent/DKMS source from at VM-create time. That
branch is now merged, so point it at main.
2026-05-30 15:45:51 -04:00
James Stringer 0e1ad3cbe3 Remove dead vhdx_create_differencing_resized (no callers)
The resized differencing-create helper was only used by the removed
cloud-image flow (it stretched a small cached cloud-image base to the
user-requested disk size). Confirmed zero callers repo-wide. Delete the
definition (disk_util.c) + declaration (disk_util.h) and drop the comment
reference in iso-patch.c's raw_to_vhdx. The plain vhdx_create_differencing
stays — still used by the template + snapshot paths. Both binaries rebuild
clean.
2026-05-30 15:01:40 -04:00
James Stringer 5a3494501c Scrub remaining cloud-image/cidata references from comments (comments only)
The cloud-image install flow is gone; reword the last comments that still
referenced it:
  - asb_core.c: "Linux cloud-image creates" -> "Linux ISO creates"
  - disk_util.c: drop the "Ubuntu cloud-image parent ~2.5 GB" rationale
    from vhdx_create_differencing_resized (now describes the generic
    inherit-then-resize behavior)
  - disk_util.h: stage_linux_agent_and_extras doc no longer says "same
    content the cidata builder used to ship"
  - appsandbox-agent.c: "autoinstall lets the user pick" -> "the create-VM
    modal lets the user pick"
2026-05-30 14:49:35 -04:00
James Stringer e3695e5da4 Fix stale Secure Boot comment for Linux VMs (comment only)
The comment claimed Secure Boot was "temporarily disabled while debugging
the direct-ISO->VHDX boot flow (POC also ran with Skip)". The code never
applies a Secure Boot template for Linux — the only non-test-mode path
that does is gated on is_windows. Reword to state the real reason: the
direct-ISO build produces an unsigned GRUB/kernel chain, so Secure Boot
would block boot.
2026-05-30 14:46:29 -04:00
James Stringer 52f71115ea Pre-merge cleanup: remove dead cidata/cloud-image flow + fix stale comments
Delete the abandoned cloud-image/cidata Linux install path (zero live
callers — linux_create_thread builds the VHDX directly from the ISO):
  - disk_util.c: drop iso_create_resources_ubuntu, generate_autoinstall_*,
    generate_network_config, generate_ubuntu_setup_sh, find_repo_root +
    copy helpers, the DEAD_ #if0 block, and the entire qcow2/VHD/inflate/
    cloud-image converter incl. ensure_ubuntu_cloud_image_cached
    (4492 -> 2315 lines). Shared helpers kept (create_iso_from_dir,
    remove_staging_dir, unix_password_hash, stage_linux_agent_and_extras).
  - disk_util.h: drop the two now-dead prototypes.

Rename the guest first-boot identifier poc-firstboot -> appsandbox-firstboot
(service/script/marker) in ubuntu_vhdx.c and reword stale "POC gpt.c"/
"f361def"/"old flow"/"subiquity"/"cidata" comment references to describe
the current direct-ISO->VHDX flow.

Fix factually-wrong comments found in a line-by-line audit:
  - asb_core.c: NAT comment claimed DHCP; it's a static IP baked into the
    HCN endpoint + pushed by the agent. SHA-512-crypt / Secure Boot /
    create-path comments reworded off the removed flow.
  - appsandbox-agent.c: gdm UID-range claim (gdm is <1000); netplan
    "renderer detect" actually probes the NetworkManager service.
  - appsandbox-display.c: cursor cadence said 30fps (TARGET_FPS is 60);
    card-selection "connected output" claim corrected (driver-name match).
  - appsandbox-audio.c: wire-proto doc said 16-bit/4-align (it's 32/8);
    snd-aloop subdev comment.

Move the internal IMPLEMENTATION_PLAN.md design doc out of the repo
(it now lives in the gitignored docs/).
2026-05-30 14:36:56 -04:00
James Stringer 4e45f8ef93 Host-derived locale/keyboard/timezone/hostname + Linux cred validation + IDD audio fix
Linux guest provisioning now mirrors the host's regional settings instead
of hardcoding en_US / us / America/New_York / "ubuntu":

  - detect_host_locale_settings() reads the current Windows user's locale
    (GetUserDefaultLocaleName), keyboard (GetKeyboardLayoutList) and
    timezone (GetDynamicTimeZoneInformation), translating each to the
    Linux equivalent. Language is gated to the 8 ISO-preinstalled
    languages (de/en/es/fr/it/pt/ru/zh) and falls back to en_US.UTF-8
    otherwise; keyboard + timezone always match the host (every XKB
    layout and IANA zone ships in the squashfs offline).
  - VM name flows through as the guest hostname (mirrors Windows
    ComputerName), staged as /etc/appsandbox-hostname.
  - All four values staged as marker files; firstboot STEP 2/3/4 read
    them with safe fallbacks and STEP 17 echoes the applied values.

Create-VM UI validation is now per-guest-OS, keyed consistently off
osType across validateVmName / validateUsername / validatePassword:
  - Linux hostname: lowercase, RFC-1123 charset
  - Linux username: ^[a-z_][a-z0-9_-]*$, <=32 chars
  - Linux password: all chars allowed (host $6$ hash path), <=255 bytes
  - default name lowercases to "myappsandbox" when Linux is selected
  - OS Type moved above VM Name in the modal; .field-warn now wraps so
    long messages don't break the modal layout; added admin-pass-warn span

IDD audio: drop the unused GetMixFormat call that returned
AUDCLNT_E_UNSUPPORTED_FORMAT on some AMD endpoints and needlessly killed
the audio path. renderfmt is built from the guest header and Initialize
runs with AUTOCONVERTPCM, so the host mix format was never used.
2026-05-30 12:59:54 -04:00
James Stringer cc9368ad61 Merge remote-tracking branch 'origin/main' into wip-linux-vm-support 2026-05-28 19:37:45 -04:00
James Stringer 2dee042922 IDD display: hotkey passthrough modes + stuck-key fix
Add two keyboard modes to the IDD display window:

- Default mode: reserved host hotkeys (Win, Alt+Tab, Alt+Esc, Ctrl+Esc,
  PrintScreen, App key) are neither forwarded to the guest nor consumed -
  they fall through to DefWindowProc so the host handles them, and the
  guest never sees a half-pressed hotkey.
- Transmit mode: toggled from the window system menu and persisted per-VM
  in display_settings.json (created lazily beside vm_state.json). While the
  window is the active foreground window, a WH_KEYBOARD_LL hook captures
  reserved hotkeys, suppresses host handling, and forwards them to the guest.

Fix the stuck Windows key: keyboard forwarding is now gated on window
activation instead of mouse-in-render-area, held keys are tracked, and any
keys still down are released to the guest on WM_ACTIVATE(WA_INACTIVE) - e.g.
clicking off or Alt+Tabbing away with a key held.

Also fix the Release post-build for the six agent tools: create
release\resources before xcopy (matching the driver-package mkdir guard) so
the copy no longer hits an interactive F/D prompt and fails with exit 2.
2026-05-28 19:26:13 -04:00
James Stringer 434bfb64ab Build-on-VM provisioning + modal-driven SSH credentials
Replace the prebuilt-ELF/prebuilt-.ko shipping model with three
in-process prefetchers that populate per-VM staging from authoritative
sources at create time:

  --prefetch-repo         GitHub tarball (agent + DKMS sources, units)
  --prefetch-build-deps   archive.ubuntu.com Packages closure walk
                          (xz-embedded for Packages.xz, BCrypt SHA-256
                          verify, synthetic Packages file for offline apt)
  --prefetch-wsl-deps     Microsoft NuGet libd3d12*/libdxcore.so

The Ubuntu guest's firstboot script then builds the agent from source
(make + make install), installs both kernel modules via DKMS, and
optionally enables openssh-server — all offline, against an isolated
apt sources dir pinned to /opt/appsandbox/local-apt{,-extras}.

xz-embedded gained CRC64 support (XZ_USE_CRC64 + xz_crc64.c compiled in)
because Ubuntu's Packages.xz uses 0x04/CRC64 in its stream check; without
this the decoder bails at the first block header. Decompression also
streams to disk in 4 MiB chunks instead of buffering the whole ~50 MiB
output in RAM.

Linux VM credentials now flow from the create-VM modal:
  - host hashes admin_pass into glibc $6$ SHA-512 (unix_password_hash,
    same path Windows autounattend uses); plaintext is wiped immediately
  - two marker files staged into the rootfs:
      /etc/appsandbox-admin-user   (UTF-8 username)
      /etc/appsandbox-admin-hash   (consumed and wiped by STEP 5)
  - firstboot useradd + usermod -p so /etc/shadow gets the hash directly
  - gdm autologin + GNOME OOBE skip now use $ASB_USER, not hardcoded test
  - SSH gated on /etc/appsandbox-ssh-enabled marker; STEP 7.5 installs
    openssh-server from local-apt-extras, enables ssh.service + ssh.socket,
    opens ufw

ComPorts/com1 restored as a Linux-only debug pipe — systemd's
console output goes to \.\pipe\<vm>.com1 so the host can see firstboot
progress without an SSH session into the guest.

STEP 17 verification now resolves the asb_drm card by walking
/sys/class/drm/card*/device/driver instead of hardcoding card0 — with
efifb=off and asb-evict-simpledrm.service, asb_drm lands as card1.
2026-05-27 21:53:46 -04:00
James Stringer 2135f5cbeb Direct Ubuntu ISO->VHDX build (no subiquity, no cidata)
iso-patch.exe gains a new --ubuntu-to-vhdx mode that produces a fully
installed, bootable Ubuntu VHDX in one shot:
  - vendored xz-embedded + custom ext4 writer + squashfs reader
  - mounts the ISO, partitions GPT (ESP + Linux root), streams
    casper/minimal.squashfs into a freshly-built ext4 rootfs through
    a producer/N-decompress-worker/single-writer pipeline
  - installs signed shim + grub from the ISO's pool .debs onto ESP,
    writes a redirect grub.cfg pointing at the rootfs by UUID
  - plants a defensive one-shot first-boot systemd unit that runs
    grub-install + update-grub + AppSandbox guest setup (agent ELFs,
    kernel modules, systemd units, wsl-mesa, wsl-deps, ldconfig,
    no-sleep dconf, etc.) and reboots

linux_create_thread now spawns iso-patch with --ubuntu-to-vhdx
and a --stage manifest covering the full extras tree (reuses
stage_linux_agent_and_extras to populate a staging dir then walks
it). image_path is cleared post-build so the ISO isn't re-attached
to the runtime VM. ComPorts/com1 named pipe and the do_connect_rdp
window path are dropped (IDD is the only live display path now).

VideoMonitor stays unconditional (vmwp.exe crashes 0xC0000005
without it) — but with no ConnectionOptions/named pipe, matching
master.

JS frontend: "Installing Linux" branch added so the spinner label
no longer says "Installing Windows" for Linux VMs. Progress %
now tracks ingest fraction (n_processed / total_inodes) mapped
into 10..75%, with post-ingest phases at 82..99%.
2026-05-25 22:34:57 -04:00
James Stringer f361defee6 Disable display blank and suspend in Linux guest provisioning
setup.sh section 9: mask the four sleep targets, drop a logind
config that ignores idle-action and lid-switch, and write a dconf
system-db with idle-delay=0, lock-enabled=false, idle-dim=false,
sleep-inactive-*-type='nothing', and power-button-action='nothing'.

Ubuntu Desktop's default 5-minute screen blank made a running VM
indistinguishable from a crashed one in the host UI, and an
auto-suspended guest stops servicing the agent's vsock heartbeat.
2026-05-25 12:04:26 -04:00
James Stringer ef531f652e Gate VideoMonitor on Linux guests only
Windows VMs were crashing vmwp.exe with STATUS_ACCESS_VIOLATION when
VideoMonitor was present without the structured Chipset.Uefi.BootThis
shape hcsshim uses for WCOW. Linux guests still need the 1024x768
display + BasicSession pipe for the RDP/console path, so gate the
VideoMonitor JSON section on !is_windows.
2026-05-25 12:04:20 -04:00
James Stringer 194cadeaa6 NVENC/NVDEC userspace path + nvidia-smi shortcut
- systemd-user environment generator and appsandbox-gpu wrapper both
  append /usr/lib/wsl/lib to LD_LIBRARY_PATH. ldconfig has the .so.1
  entries cached, but the 9P mount is read-only so the unversioned
  symlinks ldconfig wants to create can't be written. dlopen() of
  unversioned names (libcuda.so, libnvidia-encode.so, libnvcuvid.so)
  needs the path on LD_LIBRARY_PATH — without it ffmpeg's h264_nvenc /
  h264_cuvid plugins fail with 'Cannot load libcuda.so.1'.

- setup.sh: symlink /usr/lib/wsl/lib/nvidia-smi to /usr/local/bin so
  the bare command works without env tweaks. Created unconditionally
  because the symlink target is resolved at exec time — the file
  appears under the agent's 9P mount on first boot regardless of when
  the symlink was created.
2026-05-19 22:09:52 -04:00
James Stringer 2309cec029 Add Linux VM support with GPU-PV
Ubuntu 26.04 LTS guest end-to-end:

- cidata-driven autoinstall pipeline (subiquity user-data + nested
  cloud-init bootcmd) in disk_util.c. Static NAT IP, ssh-via-vsock
  marker, serial-debug kernel cmdline injected into ESP grub.cfg by
  iso-patch.exe. Five separate firstboot pipeline fixes get the VM
  to GDM unattended on Hyper-V Gen 2.

- Linux guest stack under tools/linux/:
    - appsandbox-agent  (vsock :1 control + clipboard helper spawn)
    - appsandbox-audio  (snd-aloop -> vsock :4)
    - appsandbox-display (KMS framebuffer capture -> vsock :2)
    - appsandbox-input  (uinput injection <- vsock :3)
    - appsandbox-clipboard (XCB selection bridge <-> vsock :5/:6)
  Helper binaries staged via release/resources/linux/.

- asb_drm DRM module: virtual single-CRTC display sink the host
  captures over vsock. Platform-bus, DRIVER_GEM/MODESET/ATOMIC/RENDER,
  linear shmem framebuffers. asb-evict-simpledrm.service unbinds the
  EFI simple-framebuffer before display-manager so mutter binds
  asb_drm as the sole DRM card.

- dxgkrnl vendored byte-identical from microsoft/WSL2-Linux-Kernel
  linux-msft-wsl-6.18.y with our DKMS/out-of-tree shim (dxgkrnl_compat.h
  + Kbuild + dkms.conf). Autoinstall late-commands dkms-builds against
  every /target/lib/modules/<kver> so kernel-version drift between ISO
  and post-update reboot still leaves a working .ko.

- wsl-mesa custom build (Mesa 25.3 + d3d12 gallium + microsoft-
  experimental dzn Vulkan). Installs to /opt/wsl-mesa parallel to
  Ubuntu's stock Mesa. Prebuilt tarball for ubuntu-26.04-amd64
  shipped under tools/linux/wsl-mesa/prebuilt/ (21 MB).

- Per-VM GPU acceleration policy:
    - /etc/systemd/user-environment-generators/50-appsandbox-gpu
      emits GALLIUM_DRIVER=d3d12 + LD_LIBRARY_PATH + VK_DRIVER_FILES
      into the user@1000 systemd-user environment when /dev/dxg and
      /opt/wsl-mesa are present. Inherited by every app-*.scope a
      .desktop launch creates.
    - /etc/systemd/user/org.gnome.Shell@.service.d/no-gpu.conf
      UnsetEnvironment= strips these for the compositor — mutter
      stays on llvmpipe so asb_drm scanout works (the d3d12 pipe has
      no GBM compatibility with asb_drm).
    - /usr/local/bin/appsandbox-gpu wrapper for terminal-launched
      opt-in GPU acceleration (probes /dev/dxg; no-op on no-GPU VMs).

- Host changes:
    - vm_display_idd.c, vm_clipboard.c, vm_agent.c, vm_ssh_proxy.c,
      asb_display.c: hcs_service_guid(os_type, port) so the same code
      paths reach the per-OS service identifiers (HV_GUID for Windows,
      vsock template for Linux).
    - asb_core.c: linux_create_thread builds an empty VHDX, cidata
      ISO with extras (agent ELFs, dxgkrnl src, asb_drm src, wsl-mesa
      tarball, wsl-deps libs), and boots the desktop ISO + cidata.
    - hcs_vm.c: HostLxssLib Plan9 share appended for Linux VMs;
      VideoMonitor + ComPorts retained for serial debug.
    - gpu_enum.c/h: gpu_append_lxsslib_share helper.
    - vm_clipboard.c file-drop support (CF_HDROP / x-special/gnome-
      copied-files round-trip).
    - vm_display_idd.c: 32-bit S32_LE PCM audio path matches the
      Linux daemon's snd-aloop capture format.

- iso-patch.c gains a patch_ubuntu_esp_grub_for_debug pass that
  rewrites /EFI/ubuntu/grub.cfg via the FAT32 ESP, plus a qcow2->
  vhdx converter with a RFC 1951 DEFLATE inflater for compressed
  cluster handling.

- wsl-deps: Microsoft's libd3d12.so / libd3d12core.so / libdxcore.so
  fetched on demand from the public NuGet feed
  pkgs.dev.azure.com/shine-oss/wsl (no auth). Staged at
  /opt/appsandbox/wsl-deps on the guest and put on /etc/ld.so.conf.d
  rather than /usr/lib/wsl/lib (which is overlaid by the agent's
  9P mount).

UI: web/{app.js,index.html} expose Linux VM creation + GPU selection,
distribution dropdown for cached Ubuntu ISOs, and the ISO picker for
custom images.
2026-05-19 20:21:43 -04:00
James Stringer f8ec287963 Delete orphan HCN endpoint when VM creation fails; retry at all sites
Two related bugs surfaced by the user's create-fails-then-retry sequence:

1. When hcs_create_vm_with_endpoint fails after we already created the
   HCN endpoint (e.g. WBEM_E_INVALID_PARAMETER from a transient HCS
   error, or insufficient RAM surfaced through HCS), the endpoint was
   never deleted. The IP reservation lingered in HNS even though the
   endpoint owner VM never came into existence. The next attempt at the
   same IP failed with HCN_E_ADDR_INVALID_OR_RESERVED (0x803B002F).

   Fixed by deleting the endpoint in every HCS-create or hcs_start_vm
   failure path that follows a successful endpoint create, across all
   three call sites (start_vm_thread, vhdx_create_thread, asb_vm_create).

2. Master had the retry-on-RESERVED loop only at start_vm_thread.
   vhdx_create_thread and asb_vm_create created endpoints with no
   retry, so even when the phantom reservation existed, those paths
   silently fell back to creating the VM without a network.

   Factored the retry into try_endpoint_with_retry() and used it at
   all three call sites.
2026-05-17 12:31:21 -04:00
James Stringer 5081b68dae Stable-ID handles for long-lived per-VM threads
Root-cause fix for the bug that dc3fefa band-aided: g_vms[] is compacted
(shift+ZeroMemory) when a VM or template at a lower index is removed,
which silently invalidates any background thread's cached VmInstance*.

Affected threads were the agent connection (vm_agent.c::agent_thread_proc)
and the IDD probe (asb_core.c::idd_probe_thread_proc). The probe also
posted a stale VmInstance* to the UI via WM_VM_IDD_READY.

Fix:

* VmInstance gains a UINT64 unique_id, set on creation from a process-
  global monotonic counter, never reused. The id rides along when an
  entry is shifted (it's part of the struct that gets memcpy'd) and is
  zeroed when a slot is freed. asb_find_vm_by_id() returns the current
  &g_vms[i] for that id or NULL if the VM was deleted.

* AgentConn now stores vm_id instead of vm. agent_thread_proc resolves
  via asb_find_vm_by_id at every iteration; loop exits cleanly when
  the id no longer maps to a live VM.

* idd_probe_thread_proc is launched with the id (cast through ULONG_PTR)
  and resolves every iteration the same way. WM_VM_IDD_READY now
  carries the id in LPARAM; the UI handler resolves it freshly.

Obsoletes dc3fefa's symptom-level name-guard, which is reverted in the
next commit.
2026-05-17 12:12:32 -04:00
James Stringer b8c35d51b4 Use static 192.168.42.0/24 NAT subnet (with 1 fallback candidate)
Replaces master's hardcoded 172.20.0.0/16, which overlapped the
built-in Default Switch (172.20.48.0/20) on every Windows 11 host.

The chosen subnet is decided once per process from a tiny static
candidate list:

    192.168.42.0/24  (default)
    192.168.142.0/24 (fallback if .42 conflicts with a host adapter)

Picked by collect_inuse_subnets() + ranges_overlap() at first call,
stable for process lifetime. If both candidates collide with a host
adapter (rare), we fall through to the first and log a clear warning
pointing at the candidate list in hcn_network.c.

No saved-state coordination, no per-VM seed, no HCN endpoint sweep --
master's startup network-delete (hcn_cleanup_stale_networks) wipes the
prior run's network so the freshly-picked subnet is always honored.

Replaces the over-engineered runtime picker that was reverted in the
previous two commits.
2026-05-17 12:04:37 -04:00
James Stringer c0afcba3f3 Revert "Auto-pick NAT subnet at runtime to avoid host-adapter overlap"
This reverts commit afab17b9e0.
2026-05-17 12:01:04 -04:00
James Stringer 9189c9e77d Revert "Stop deleting HCN networks at startup; sweep stale endpoints instead"
This reverts commit 423b9a75a4.
2026-05-17 12:00:58 -04:00
James Stringer dc3fefa3f5 Guard agent/probe threads against zeroed g_vms[] slots
When a VM or template at a lower index is removed (deleted, or template
finishes sysprep), the g_vms[] array is compacted: entries shift down
and the now-vacated last slot is zeroed. Any background thread that
cached its VM pointer as &g_vms[idx] before the shift is left pointing
at the wrong slot (either zeroed or holding another VM's data).

Concrete repro: create a template, then create a regular VM while the
template is still building. The regular VM's agent thread holds
&g_vms[1]. When the template at g_vms[0] finishes and gets removed,
the regular VM's data shifts to g_vms[0] and &g_vms[1] is zeroed. The
agent thread now loops every few seconds calling
hcs_find_runtime_id(L"", ...) -- which logs the symptom the user saw:

  HCS enumerate: [{...running VM...}]
  hcs_find_runtime_id: VM "" not found in enumeration

Surgical fix: the agent loop and IDD-probe loop both bail out cleanly
when vm->name[0] is empty, which is the post-zero state. Stops the
log spam and the wasted IOCTLs.

This is a defensive guard, not a root-cause fix. The deeper issue is
that g_vms[] uses unstable indices: any cached VmInstance* can be
invalidated by compaction. A proper fix replaces shift+truncate with
stable slots marked as deleted, which is a larger refactor.
2026-05-17 11:46:19 -04:00
James Stringer 423b9a75a4 Stop deleting HCN networks at startup; sweep stale endpoints instead
Previously, hcn_cleanup_stale_networks() at app init blindly deleted our
three fixed-GUID networks. That had two bad effects:

1. The vEthernet adapter for AppSandboxNAT was destroyed on every start,
   forcing a re-pick of the subnet. If a saved VM existed (with a
   previously-allocated 192.168.x.y), and the picker -- not knowing the
   old subnet -- happened to choose a different one, the new endpoint
   create would fail with HCN_E_ADDR_INVALID_OR_RESERVED because the
   IP wasn't in the live network's range.

2. asb_cleanup() at app exit terminates VMs but does not call
   asb_vm_cleanup_network. It relied on the async HCS SystemExited
   callback to fire asb_hcs_state_changed -> asb_vm_cleanup_network for
   each VM, but the process often exited before the callback completed.
   The startup network-delete was the safety net catching the resulting
   orphans -- which is exactly the destructive behavior in (1).

Replaced with:

* hcn_cleanup_stale_endpoints() at startup. Enumerates every HCN endpoint,
  uses HcnQueryEndpointProperties to verify it belongs to one of our
  three networks (so we never touch foreign endpoints), and deletes
  just those. Networks themselves are left intact.

* hcn_seed_nat_subnet_from_ip() called from asb_init after load_vm_list
  with the first saved VM's IP. The picker adopts that subnet so the
  globals match the live network across restarts.

* asb_cleanup now calls asb_vm_cleanup_network for each VM after
  hcs_terminate_vm. This is the reference-counted teardown that already
  existed for clean per-VM stops; it deletes the endpoint and (only when
  no other VM uses the shared network) the network. The adapter goes
  away cleanly on clean exit and the next startup will pick a fresh
  subnet -- no leak.

* try_create_endpoint_with_retry() factors the increment-on-fail loop
  out of start_vm_thread and applies it to the two other endpoint-create
  sites (vhdx_create_thread, asb_vm_create). Defense in depth in case
  HNS ever holds a phantom reservation past the endpoint sweep.
2026-05-17 11:33:27 -04:00
James Stringer afab17b9e0 Auto-pick NAT subnet at runtime to avoid host-adapter overlap
Default Switch ships on every Windows 11 at 172.20.48.0/20, which our
hardcoded 172.20.0.0/16 superseded. HCN rejected the create with
ERROR_DUP_NAME (Microsoft re-uses that error code for subnet conflicts;
the user-facing message is misleading).

New behavior: on first NAT VM start of a process, enumerate every IPv4
subnet bound to a host adapter and pick the first 192.168.N.0/24 with
N>=42 that does not overlap any of them. Cached for process lifetime.

Consumers (allocate_nat_ip, set_ip agent command, endpoint create) now
read the picked prefix/gateway/base via hcn_nat_* accessors instead of
hardcoded 172.20 literals.
2026-05-17 10:33:12 -04:00
James Stringer 15bbf36f36 Retry next NAT IP when endpoint creation fails 2026-05-05 19:32:37 -04:00
James Stringer 7d76acdf52 Make downloads more reliable on macOS 2026-04-22 22:43:37 -04:00
James Stringer 228a3095a4 Fix unable to interrupt download 2026-04-22 21:48:16 -04:00
James Stringer f7a6878380 Add a new try all GPU mode (hcs gpu mirror) 2026-04-21 18:31:16 -04:00
James Stringer 5bfe945c80 improve windows clipboard 2026-04-20 21:19:56 -04:00
James Stringer 97c0d4c17d don't sync clipboard unless vm window open + mute vm on window close 2026-04-19 19:49:42 -04:00
James Stringer e642f97e75 Improve clipboard, don't sleep displays 2026-04-19 17:32:53 -04:00
James Stringer 8902aefbab handle shutdown better 2026-04-18 23:38:47 -04:00
James Stringer ad551fb39e Set Computer Name 2026-04-18 22:59:38 -04:00
James Stringer aad2902c0a SSH + Clipboard + File staging for macOS 2026-04-18 22:14:59 -04:00
James Stringer 4c458048c8 don't auto start for now 2026-04-17 20:51:34 -04:00
James Stringer 79dd88d85c Update vz_install.m 2026-04-17 20:28:59 -04:00
James Stringer 19e0425d3a fix attempt number 1 billion 2026-04-17 20:26:21 -04:00
James Stringer 44e9b616db fix auto start after install 2026-04-17 20:16:33 -04:00
James Stringer f121d26b24 Update vz_install.m 2026-04-17 20:06:51 -04:00
James Stringer f716f68a39 log improvements 2026-04-17 19:59:40 -04:00
James Stringer fadcbd29d3 fixes 2026-04-17 19:43:06 -04:00
James Stringer adf98fad45 Update ui.m 2026-04-17 18:43:07 -04:00