mirror of
https://github.com/jamesstringer90/appsandbox.git
synced 2026-10-22 02:52:49 -05:00
Win-on-Mac: fix ivshmem AGENT/SSH region overlap and display readiness
Post-review fixes and release prep for the win-on-mac feature. * asb_shm_layout.h: fix a region-sizing bug -- the AGENT (ch1) slot stride (0x20070) exceeded ASB_AGENT_REGION_SIZE (0x20000) by 112 bytes, so the agent host->guest ring overran into the adjacent SSH (ch7) region and corrupted its slot state under sustained traffic. Enlarge the AGENT region to 0x21000 (downstream offsets cascade) and add per-region compile-time guards (n_slots*stride <= region_size) so this class of overlap fails the build. Host/publisher-side only -- the guest reads geometry from the published directory. * headless.m: GET /display readiness now includes idd_ready for a Windows guest, matching the open precondition (asb_core_mac.m) and the Windows host (asb_vm_idd_ready). Before, the Mac readiness was running && agent_online only, so it could report ready before the VDD was up and a poll-then-open would spuriously 409 display_not_ready. macOS guests are unchanged. * iso-patch-mac.m (stage): wipe the plaintext password buffer on the --shadowhash-file read-failure path, matching the sibling --kcpassword-file path. * iso_patch_mac.m: point the signed Windows guest payload at the v0.1.4 release. * vm_lifecycle.py: raise the Windows build-stall budget to 3600 s for the signal-less first-boot window under concurrent multi-VM load. * tools/headless-api/README.md: document Windows guests on macOS hosts (platform matrix, host-fixed line, requirements heading, osType field).
This commit is contained in:
1 parent
96fbc9ae60
commit
dfd6fe475f
6 files changed
+32
-16
No files matched your search
@@ -33,6 +33,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AppKit/AppKit.h> /* NSApplication run loop (display windows in a GUI session) */
|
||||
#import <Virtualization/Virtualization.h>
|
||||
#include <strings.h> /* strcasecmp */
|
||||
#import "asb_core_mac.h"
|
||||
#import "vz_display.h" /* VzDisplayWindow.userClosed (displayOpen status) */
|
||||
#import "host_info.h"
|
||||
@@ -941,15 +942,17 @@ static int handle_request(int fd, HttpReq *r) {
|
||||
return 0;
|
||||
}
|
||||
if (isGET) {
|
||||
/* Pollable + passive: reads flags, opens no window, touches no
|
||||
frame channel. macOS binds the in-process framebuffer directly,
|
||||
so ready == running && agent_online -- nothing to probe. */
|
||||
/* Pollable + passive: reads flags, opens no window, touches no frame channel.
|
||||
A macOS guest binds the in-process framebuffer (ready == running && agent_online);
|
||||
a Windows guest streams VDD frames over ivshmem and is ready only once its IDD
|
||||
driver is up (idd_ready) -- matching the open precondition (asb_mac_open_display). */
|
||||
__block BOOL open = NO, ready = NO;
|
||||
on_main(^{
|
||||
AsbVmMac *v = asb_mac_vm_find(name.UTF8String);
|
||||
if (!v) return;
|
||||
BOOL is_win = (strcasecmp(v->os_type, "Windows") == 0);
|
||||
open = (v->display != nil && !v->display.userClosed);
|
||||
ready = open || (v->running && v->agent_online);
|
||||
ready = open || (v->running && v->agent_online && (!is_win || v->idd_ready));
|
||||
});
|
||||
send_json(fd, 200, "OK",
|
||||
json_str(@{ @"open": open ? @YES : @NO,
|
||||
|
||||
@@ -548,11 +548,10 @@ static BOOL write_prov_scripts(NSString *dir, NSString *sshMsiName) {
|
||||
* the root daemon + GUI. build-windows extracts the needed members into its own per-build temp. */
|
||||
+ (nullable NSString *)ensureSignedWinPayloadZipCached {
|
||||
/* Signed ARM64 release: EV-signed agent EXEs + MS-attestation-signed drivers (VDD/VAD/AppSandboxSHM/
|
||||
* devcon). The GitHub attachment id is embedded in the cached filename, so changing the URL below
|
||||
* (even at the same app version) auto-invalidates the cache. The URL must stay publicly fetchable
|
||||
* by an unauthenticated client. */
|
||||
NSString *name = @"AppSandbox-0.1.3-win-arm64-29486776.zip";
|
||||
NSString *urlStr = @"https://github.com/user-attachments/files/29486776/AppSandbox-0.1.3-win-arm64.zip";
|
||||
* devcon). The release version is pinned in the cached filename, so a newer release auto-invalidates
|
||||
* the cache. The URL must stay publicly fetchable by an unauthenticated client. */
|
||||
NSString *name = @"AppSandbox-0.1.4-win-arm64.zip";
|
||||
NSString *urlStr = @"https://github.com/jamesstringer90/appsandbox/releases/download/v0.1.4/AppSandbox-0.1.4-win-arm64.zip";
|
||||
NSString *cacheDir = [[VmDir vmsRootDirectory] URLByDeletingLastPathComponent].path;
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:cacheDir
|
||||
withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
|
||||
@@ -69,7 +69,7 @@ in create-validation (wrong `osType` → `400`); the host-feature row mirrors
|
||||
|
||||
| | Windows host | macOS host |
|
||||
|---|:---:|:---:|
|
||||
| **Windows guest** | ✅ | ❌ |
|
||||
| **Windows guest** | ✅ | ✅ (Apple silicon) |
|
||||
| **Linux guest** (Ubuntu) | ✅ | ❌ |
|
||||
| **macOS guest** | ❌ | ✅ (Apple silicon) |
|
||||
| Snapshots & branches | ✅ | ❌ `501` |
|
||||
@@ -78,7 +78,7 @@ in create-validation (wrong `osType` → `400`); the host-feature row mirrors
|
||||
| GPU, NAT/network modes, SSH server, SSH-key auto-deploy, SSE events | ✅ | ✅ |
|
||||
|
||||
Only `snapshots`/`templates` are advertised in the `capabilities` object; guest
|
||||
OS is host-fixed (Windows host → Windows + Linux; macOS host → macOS only), so
|
||||
OS is host-fixed (Windows host → Windows + Linux; macOS host → macOS + Windows), so
|
||||
a client picks its `osType` from `version()["hostOs"]`, not from `capabilities`.
|
||||
|
||||
## Requirements
|
||||
@@ -91,7 +91,7 @@ a client picks its `osType` from `version()["hostOs"]`, not from `capabilities`.
|
||||
| **Administrator** | HCS VM operations and the `http.sys` bind both require elevation. |
|
||||
| **`VirtualMachinePlatform` feature** | The HCS backend the core uses. Enable with `dism /online /Enable-Feature /FeatureName:VirtualMachinePlatform /All` then reboot. |
|
||||
|
||||
**macOS host** (runs macOS guests only — Virtualization.framework):
|
||||
**macOS host** (runs macOS guests via Virtualization.framework, and Windows guests via QEMU+HVF):
|
||||
|
||||
| Requirement | Why |
|
||||
|---|---|
|
||||
@@ -303,7 +303,7 @@ rather than forwarding bad input to the core.
|
||||
| Key | Notes |
|
||||
|---|---|
|
||||
| `name` | guest hostname; **set at create, not editable later**. Letters/digits/hyphens; not all-digits; no leading/trailing hyphen. Windows ≤15 chars (NetBIOS); Linux ≤63 and lowercase. Must be unique across VMs *and* templates. |
|
||||
| `osType` | `"Linux"` or `"Windows"` |
|
||||
| `osType` | `"Linux"` or `"Windows"` on a Windows host; `"macOS"` or `"Windows"` on a macOS host. |
|
||||
| `imagePath` | path to an install ISO — **required unless** `templateName` is given |
|
||||
| `templateName` | create from a Windows template instead of an ISO |
|
||||
| `ramMb` | ≥512, **2 MB-aligned** (even). The SDK rounds down to even for you. |
|
||||
|
||||
@@ -55,7 +55,7 @@ def run_vm_lifecycle(spec):
|
||||
# signal-less window. It can run 10+ minutes (longer under concurrent
|
||||
# multi-VM load), so give Windows a real budget; Linux/macOS stream live
|
||||
# progress and keep the tight 600 s stall.
|
||||
BUILD_STALL = 1800 if os_type == "Windows" else 600
|
||||
BUILD_STALL = 3600 if os_type == "Windows" else 600
|
||||
|
||||
c = asb.connect()
|
||||
caps = c.version().get("capabilities", {})
|
||||
|
||||
@@ -790,7 +790,7 @@ static int cmd_stage(int argc, char **argv) {
|
||||
NSData *shdData = nil, *kcData = nil;
|
||||
if (shadowhashFile) {
|
||||
shdData = [NSData dataWithContentsOfFile:shadowhashFile];
|
||||
if (!shdData) { emit_error(@"stage: cannot read --shadowhash-file"); return 3; }
|
||||
if (!shdData) { memset(passBuf, 0, sizeof(passBuf)); emit_error(@"stage: cannot read --shadowhash-file"); return 3; }
|
||||
} else if (passLen > 0) {
|
||||
shdData = asb_macos_shadow_hash_data(passBuf, passLen);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
#define ASB_CLIP_RING_CAP 0x100000u /* 1 MiB per ring */
|
||||
|
||||
#define ASB_AGENT_REGION_OFF (ASB_CLIPR_REGION_OFF + ASB_CLIPR_REGION_SIZE) /* ch1 */
|
||||
#define ASB_AGENT_REGION_SIZE 0x20000ull /* 128 KiB */
|
||||
#define ASB_AGENT_REGION_SIZE 0x21000ull /* 132 KiB -- must be >= ASB_AGENT_SLOT_STRIDE so the slot fits the region */
|
||||
#define ASB_AGENT_RING_CAP 0x10000u /* 64 KiB per ring */
|
||||
|
||||
#define ASB_SSH_REGION_OFF (ASB_AGENT_REGION_OFF + ASB_AGENT_REGION_SIZE) /* ch7 */
|
||||
@@ -67,6 +67,20 @@
|
||||
#define ASB_SSH_SLOT_STRIDE (ASB_SLOT_HDR + 2u * (ASB_RING_HDR + ASB_SSH_RING_CAP))
|
||||
#define ASB_P9_SLOT_STRIDE (ASB_SLOT_HDR + 2u * (ASB_RING_HDR + ASB_P9_RING_CAP))
|
||||
|
||||
/* Each region must hold all its slots: n_slots * slot_stride <= region_size. These compile-time
|
||||
* checks fail the build (negative array dimension) if a region is too small to contain its slot(s),
|
||||
* so a region/ring-cap mismatch cannot silently overlap the next region. File scope (no
|
||||
* unused-typedef warning); compiled only on the host/publisher side -- the guest reads geometry
|
||||
* from the published directory. */
|
||||
typedef char asb_fits_display[(ASB_DISPLAY_SLOT_STRIDE <= ASB_FRAME_REGION_SIZE) ? 1 : -1];
|
||||
typedef char asb_fits_input [(ASB_INPUT_SLOT_STRIDE <= ASB_INPUT_REGION_SIZE) ? 1 : -1];
|
||||
typedef char asb_fits_audio [(ASB_AUDIO_SLOT_STRIDE <= ASB_AUDIO_REGION_SIZE) ? 1 : -1];
|
||||
typedef char asb_fits_clipw [(ASB_CLIP_SLOT_STRIDE <= ASB_CLIPW_REGION_SIZE) ? 1 : -1];
|
||||
typedef char asb_fits_clipr [(ASB_CLIP_SLOT_STRIDE <= ASB_CLIPR_REGION_SIZE) ? 1 : -1];
|
||||
typedef char asb_fits_agent [(ASB_AGENT_SLOT_STRIDE <= ASB_AGENT_REGION_SIZE) ? 1 : -1];
|
||||
typedef char asb_fits_ssh [((uint64_t)ASB_SSH_N_SLOTS * ASB_SSH_SLOT_STRIDE <= ASB_SSH_REGION_SIZE) ? 1 : -1];
|
||||
typedef char asb_fits_p9 [((uint64_t)ASB_P9_N_SLOTS * ASB_P9_SLOT_STRIDE <= ASB_P9_REGION_SIZE) ? 1 : -1];
|
||||
|
||||
#define ASB_SHM_N_REGIONS 8
|
||||
|
||||
/* Initialize one region's slots in place: zero each slot, set both rings' caps, mark FREE.
|
||||
|
||||
Reference in new issue
Block a user