From 988ccc8268df086283beea43c1b04df84adb7901 Mon Sep 17 00:00:00 2001 From: roboomp Date: Fri, 14 Aug 2026 08:49:26 +0000 Subject: [PATCH] fix(pi-natives): guard DeviceCheck token generation on GUI session -[DCDevice isSupported] synchronously opens an XPC connection to the per-user DeviceCheck metadata daemon, which exists only in an interactive GUI login session. From a session without graphic access (SSH, launchd LaunchDaemon, CI runner, service account, sandbox) the connection setup hits _xpc_api_misuse and aborts the process with SIGTRAP before any completion handler runs, so the promise never rejects and every openai-codex/* OAuth model becomes unusable. Check the caller's security session for the sessionHasGraphicAccess attribute via SessionGetInfo before touching DeviceCheck; resolve { supported: false, error } when it is absent, mirroring the non-macOS stub and letting the caller send an error-coded attestation instead of dying. Fixes #8353 --- crates/pi-natives/src/devicecheck.rs | 41 ++++++++++++++++++++++++++++ packages/natives/CHANGELOG.md | 4 +++ 2 files changed, 45 insertions(+) diff --git a/crates/pi-natives/src/devicecheck.rs b/crates/pi-natives/src/devicecheck.rs index 5d19b4a86..2f9396222 100644 --- a/crates/pi-natives/src/devicecheck.rs +++ b/crates/pi-natives/src/devicecheck.rs @@ -99,6 +99,43 @@ mod platform { static _NSConcreteStackBlock: *const c_void; } + // `SessionGetInfo` reports the caller's login-session attributes; consulted + // to detect a GUI/graphic session before touching the GUI-only DeviceCheck + // daemon. + #[link(name = "Security", kind = "framework")] + unsafe extern "C" { + fn SessionGetInfo(session: u32, session_id: *mut u32, attributes: *mut u32) -> i32; + } + + /// `callerSecuritySession` — query the session hosting the current process. + const CALLER_SECURITY_SESSION: u32 = u32::MAX; + /// `sessionHasGraphicAccess` attribute bit from `Security/AuthSession.h`. + const SESSION_HAS_GRAPHIC_ACCESS: u32 = 0x0010; + + /// Whether the caller's security session has GUI/graphic access. + /// + /// `DCDevice.isSupported` synchronously opens an XPC connection to the + /// per-user DeviceCheck metadata daemon, which exists only in an + /// interactive GUI login session. From a session without graphic access + /// (SSH, a launchd `LaunchDaemon`, a CI runner, a service account, a + /// sandbox) the connection setup hits `_xpc_api_misuse` and aborts the + /// whole process with `SIGTRAP` before the completion handler can run — so + /// there is no error to return, only a dead process. Gating on the + /// documented session attribute keeps the call out of that trapping path. + /// + /// Returns `false` when graphic access is absent *or* the session cannot be + /// queried: degrading to "unsupported" merely drops the attestation header + /// (exactly as on every non-macOS host), whereas optimistically assuming + /// "supported" risks the process-killing trap. + fn session_has_graphic_access() -> bool { + let mut attributes: u32 = 0; + // SAFETY: `SessionGetInfo` writes the caller session's attribute bits + // through the out-pointer; the session-id slot is unused, so it is null. + let status = + unsafe { SessionGetInfo(CALLER_SECURITY_SESSION, ptr::null_mut(), &mut attributes) }; + status == 0 && attributes & SESSION_HAS_GRAPHIC_ACCESS != 0 + } + /// Outcome delivered once from the completion block to the waiting worker. enum Completion { Token(String), @@ -289,6 +326,10 @@ mod platform { error: None, latency_ms: 0.0, }; + if !session_has_graphic_access() { + result.error = Some("DeviceCheck unavailable without a GUI login session".to_owned()); + return result; + } // SAFETY: `c"DCDevice"` is a valid null-terminated class name. let class = unsafe { objc_getClass(c"DCDevice".as_ptr()) }; if class.is_null() { diff --git a/packages/natives/CHANGELOG.md b/packages/natives/CHANGELOG.md index d9cf4c609..260b8201c 100644 --- a/packages/natives/CHANGELOG.md +++ b/packages/natives/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed `deviceCheckGenerateToken` aborting the whole process with `SIGTRAP` when called from a macOS session without GUI/graphic access (SSH, a launchd `LaunchDaemon`, a CI runner, a service account, a sandbox), which made every `openai-codex/*` OAuth model unusable for such accounts. `-[DCDevice isSupported]` synchronously opens an XPC connection to the per-user DeviceCheck metadata daemon, which exists only in an interactive GUI login session; without one the connection setup hits `_xpc_api_misuse` and traps before any completion handler runs, so the promise never rejects. The binding now checks the caller's security session for the `sessionHasGraphicAccess` attribute first and resolves `{ supported: false, error: … }` instead of touching DeviceCheck when it is absent ([#8353](https://github.com/can1357/oh-my-pi/issues/8353)). + ## [17.2.12] - 2026-08-08 ### Changed