把可能弹 UAC 的 ACL 提权修复移出 append 锁的持锁窗口

- project/agent_db.rs:Windows 取锁成功路径不再在"已持有零共享句柄"时做带提权的 ACL 修复;持锁期间只做不提权的严格校验,校验不过就先释放句柄、再提权修复,并返回"这轮没取到锁"由外层重试循环按修复后的 DACL 重新打开
- project/agent_db.rs:新增 release_project_append_os_lock_then_repair_acl,参数按值接收锁句柄且函数体第一件事是 drop,使"边持锁边等 UAC"在签名层面无法被表达
- 原因:提权修复走 powershell Start-Process -Verb RunAs -Wait 同步等用户点 UAC,在持锁期间等它等于把用户犹豫的时间记进别人的持锁窗口;只有 Windows 存在这条路径
- 判据(结构级):提权修复只在此一处被调用,且调用点必须交出句柄所有权;测试环境不弹真 UAC(config.rs 测试分支本地收紧),故不写假 UAC 用例
- 真机判据:DACL 有缺陷的机器上,持锁方进入修复期间不再持锁,等待方从"超时"变为可成功
- 不变项:首次创建锁文件的本地 harden 仍留在持锁期间(本进程新建对象不因继承 DACL 自动提权)
This commit is contained in:
2026-09-12 00:12:52 +08:00
parent 1affb5eec3
commit 77871bfa56
@@ -3714,6 +3714,22 @@ fn try_open_project_append_os_lock(path: &Path, error_label: &str) -> Result<Opt
}
}
/// 释放锁句柄之后才执行可能弹 UAC 的 ACL 提权修复。
///
/// `held_lock` 按值接收刚打开(因而正被持有)的锁句柄:调用点必须把所有权交出来,于是
/// "一边持锁一边等 UAC" 在这个签名下无法被表达。函数体第一件事就是 `drop`,之后才提权;
/// 修复成功后调用方返回"这轮没取到锁",由外层重试循环重新打开,按修复后的 DACL 通过严格校验。
#[cfg(windows)]
fn release_project_append_os_lock_then_repair_acl(
held_lock: File,
path: &Path,
strict_error: &str,
) -> Result<(), String> {
drop(held_lock);
crate::secure_windows_game_creator_path_for_current_user_with_auto_elevation(path, false, true)
.map_err(|repair_error| format!("{strict_error};自动提权修复未完成:{repair_error}"))
}
#[cfg(windows)]
fn try_open_project_append_os_lock(path: &Path, error_label: &str) -> Result<Option<File>, String> {
use std::os::windows::fs::OpenOptionsExt;
@@ -3733,15 +3749,24 @@ fn try_open_project_append_os_lock(path: &Path, error_label: &str) -> Result<Opt
Ok(file) => {
crate::runner::validate_windows_regular_file_handle(&file, error_label)?;
if !existed {
// 自建对象的本地收紧不会触发 UAC(既有口径:本进程新建的 Windows 私有对象
// 不因继承 DACL 自动提权),所以它留在持锁期间;**提权修复**不留在这里。
if let Err(error) = harden_new_game_creator_private_path(path, false, error_label) {
drop(file);
let _ = fs::remove_file(path);
return Err(error);
}
}
crate::secure_windows_game_creator_path_for_current_user_with_auto_elevation(
path, false, true,
)?;
// 严格校验留在持锁期间(对象就是刚打开的这个句柄),**修复移到锁外**:提权修复会
// `powershell.exe -Command "Start-Process -Verb RunAs -Wait …"` 同步等用户点 UAC
// config.rs 的 attempt_elevated_windows_acl_repair),在这里等它等于把"用户犹豫
// 的时间"记进别人的持锁窗口——1s 预算必然打满,而且只有 Windows 有这条路径。
if let Err(error) =
crate::secure_windows_game_creator_path_for_current_user(path, false, true)
{
release_project_append_os_lock_then_repair_acl(file, path, &error)?;
return Ok(None);
}
crate::runner::validate_windows_regular_file_handle(&file, error_label)?;
Ok(Some(file))
}