fix(sandbox): make NUL lease thread-safe

This commit is contained in:
coso
2026-08-26 18:30:28 +08:00
parent a2a6caa858
commit 37f385db68
3 changed files with 5 additions and 4 deletions
@@ -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. 阻塞与避让
@@ -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(())
}
@@ -327,7 +327,6 @@ fn dpapi_protect(plaintext: &[u8]) -> io::Result<Vec<u8>> {
#[cfg(windows)]
fn write_json_atomic<T: Serialize>(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,
};