From 37f385db6865ba45cfd6f498f9108a299b0c7977 Mon Sep 17 00:00:00 2001 From: coso Date: Wed, 26 Aug 2026 18:30:28 +0800 Subject: [PATCH] fix(sandbox): make NUL lease thread-safe --- .../exec-plans/codex-desktop-complete-alignment-plan.md | 1 + .../tool-runtime/src/execution_process/windows_null.rs | 7 ++++--- lime-rs/crates/tool-runtime/src/windows_setup.rs | 1 - 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/internal/exec-plans/codex-desktop-complete-alignment-plan.md b/internal/exec-plans/codex-desktop-complete-alignment-plan.md index a53fdde13..745f244f6 100644 --- a/internal/exec-plans/codex-desktop-complete-alignment-plan.md +++ b/internal/exec-plans/codex-desktop-complete-alignment-plan.md @@ -392,6 +392,7 @@ Renderer typed gateway - 2026-08-26:Windows Quality run `32952578959`(SHA `315c1cd79`)确认 runner transport 已越过 pipe state 切换,setup/MSVC 编译继续通过;schema v3 artifact 的七项矩阵仍为 `1/7`,六个 restricted child 均已 spawn 但在产生输出前以 code `1` 退出。对照 Codex current command runner 后确认 Lime 漏掉 restricted PowerShell/stdio 初始化所需的 NUL device ACL;Codex 对 capability SID 授权,而 Lime capability 是每回合随机 UUID,直接照搬会在全局设备 DACL 永久堆积短生命周期 ACE。当前修复改由 elevated setup 一次性向两个稳定 sandbox account SID 授予同等 NUL read/write/execute 权限,并在 readiness 对 effective rights 做 read-back;account SID 仍只作为 restricting identity,不进入 `TokenDefaultDacl`,workspace capability 边界不放宽。该 run 不改变 `44/46 (95.7%)` 或 `Planned/enforced=false`,待真机重跑验证此根因假设。 - 2026-08-26:Windows Quality run `32954904978`(SHA `5eebd985f`)验证稳定 sandbox account 的 NUL ACL 仍不足:setup/MSVC 编译通过,七项矩阵为 `1/7`,六个 restricted child 均 spawn 后立即以 exit code `1` 结束且无输出。继续对齐 Codex current:新增 `windows_null::NullDeviceLease`,用 `Local\\LimeSandboxNullDeviceAcl` named mutex 串行授予/撤销当前 capability SID 的 NUL read/write/execute ACE,lease 在 child/descendant 完成后回收;普通 pipe child 同时移除强制 `CREATE_NO_WINDOW`,恢复常规命令 inherited-console 语义。macOS 定向 related Rust `373/373`、fmt/diff check 通过;Windows-only 编译与七项真机证据待新 SHA 重跑,计划仍保持 `44/46 (95.7%)`、`active`、`Planned/enforced=false`。 - 2026-08-26:Windows Quality run `32957046834`(SHA `e147a8731`)完成 setup/MSVC 编译,但六项 restricted case 在 child spawn 前统一返回 `CreateFileW(Windows sandbox NUL): ERROR_ACCESS_DENIED`,七项仍为 `1/7`。该证据证明低权限 sandbox-account runner 不能持有 NUL 的 `WRITE_DAC`;修复将 capability NUL lease 移至宿主,在 `CreateProcessWithLogonW` 前安装 ACE,并与 workspace ACL lease 一起由 supervisor/reaper 持有到 runner 及 descendants 退出,sandbox account 不获得全局 DACL 管理权。定向 related Rust `373/373`、fmt/diff check 通过;待下一轮 Windows schema v3 真机重跑,完成度/readiness 不变。 +- 2026-08-26:Windows Quality run `32957785741`(SHA `a2a6caa85`,artifact `9602756033`)在 setup 阶段编译 `tool-runtime` 失败,七项矩阵未执行。MSVC `E0277` 明确指向 `NullDeviceLease` 持有 `LocalSid(*mut c_void)`,因此无法跨 supervisor/reaper 线程移动;修复为 lease 仅保存 capability SID 字符串,安装/撤销 ACE 时在当前线程短暂解析 `LocalSid`,不添加 `unsafe impl Send`。同时移除日志暴露的 Windows-only unused import;本机 related Rust 反向依赖链与 `tool-runtime 373/373`、rustfmt、`git diff --check` 通过。该 run 继续是 `failedStage=setup`、矩阵 `0/7`,不改变 `44/46 (95.7%)` 或 `Planned/enforced=false`。 ## 7. 阻塞与避让 diff --git a/lime-rs/crates/tool-runtime/src/execution_process/windows_null.rs b/lime-rs/crates/tool-runtime/src/execution_process/windows_null.rs index decfd2703..b30b6bf9f 100644 --- a/lime-rs/crates/tool-runtime/src/execution_process/windows_null.rs +++ b/lime-rs/crates/tool-runtime/src/execution_process/windows_null.rs @@ -27,7 +27,7 @@ const NULL_DEVICE_MUTEX: &str = "Local\\LimeSandboxNullDeviceAcl"; /// PowerShell may open NUL while initializing redirected standard streams. The capability ACE /// is removed when this lease ends so per-execution SIDs do not accumulate in the device DACL. pub(super) struct NullDeviceLease { - capability: LocalSid, + capability_sid: String, released: bool, } @@ -37,7 +37,7 @@ impl NullDeviceLease { let _guard = NullDeviceAclLock::acquire()?; update_null_device_acl(capability.raw(), SET_ACCESS)?; Ok(Self { - capability, + capability_sid: capability_sid.to_owned(), released: false, }) } @@ -46,8 +46,9 @@ impl NullDeviceLease { if self.released { return Ok(()); } + let capability = LocalSid::parse(&self.capability_sid)?; let _guard = NullDeviceAclLock::acquire()?; - update_null_device_acl(self.capability.raw(), REVOKE_ACCESS)?; + update_null_device_acl(capability.raw(), REVOKE_ACCESS)?; self.released = true; Ok(()) } diff --git a/lime-rs/crates/tool-runtime/src/windows_setup.rs b/lime-rs/crates/tool-runtime/src/windows_setup.rs index 047f647a8..f20f6b38e 100644 --- a/lime-rs/crates/tool-runtime/src/windows_setup.rs +++ b/lime-rs/crates/tool-runtime/src/windows_setup.rs @@ -327,7 +327,6 @@ fn dpapi_protect(plaintext: &[u8]) -> io::Result> { #[cfg(windows)] fn write_json_atomic(path: &Path, value: &T) -> io::Result<()> { - use std::os::windows::ffi::OsStrExt; use windows_sys::Win32::Storage::FileSystem::{ MoveFileExW, MOVEFILE_REPLACE_EXISTING, MOVEFILE_WRITE_THROUGH, };