Compare commits

...

1 Commits

Author SHA1 Message Date
David Wiesen
0fdb9f9cf3 Retry Windows sandbox runner logon failures 2026-04-20 05:04:15 -07:00

View File

@@ -40,6 +40,9 @@ mod windows_impl {
use crate::logging::log_success;
use crate::policy::SandboxPolicy;
use crate::policy::parse_policy;
use crate::setup::SandboxSetupRequest;
use crate::setup::SetupRootOverrides;
use crate::setup::run_elevated_setup;
use crate::token::convert_string_sid_to_sid;
use crate::winutil::quote_windows_arg;
use crate::winutil::resolve_sid;
@@ -222,6 +225,13 @@ mod windows_impl {
}
}
const ERROR_LOGON_FAILURE: i32 = 1326;
const ERROR_ACCOUNT_LOCKED_OUT: i32 = 1909;
fn should_retry_after_logon_failure(err: i32) -> bool {
matches!(err, ERROR_LOGON_FAILURE | ERROR_ACCOUNT_LOCKED_OUT)
}
/// Launches the command runner under the sandbox user and captures its output.
#[allow(clippy::too_many_arguments)]
pub fn run_windows_sandbox_capture(
@@ -252,7 +262,7 @@ mod windows_impl {
let logs_base_dir: Option<&Path> = Some(sandbox_base.as_path());
log_start(&command, logs_base_dir);
let sandbox_creds = require_logon_sandbox_creds(
let mut sandbox_creds = require_logon_sandbox_creds(
&policy,
sandbox_policy_cwd,
cwd,
@@ -328,9 +338,9 @@ mod windows_impl {
let mut si: STARTUPINFOW = unsafe { std::mem::zeroed() };
si.cb = std::mem::size_of::<STARTUPINFOW>() as u32;
let mut pi: PROCESS_INFORMATION = unsafe { std::mem::zeroed() };
let user_w = to_wide(&sandbox_creds.username);
let mut user_w = to_wide(&sandbox_creds.username);
let domain_w = to_wide(".");
let password_w = to_wide(&sandbox_creds.password);
let mut password_w = to_wide(&sandbox_creds.password);
// Suppress WER/UI popups from the runner process so we can collect exit codes.
let _ = unsafe { SetErrorMode(0x0001 | 0x0002) }; // SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX
@@ -345,36 +355,80 @@ mod windows_impl {
);
// Ensure command line buffer is mutable and includes the exe as argv[0].
let spawn_res = unsafe {
CreateProcessWithLogonW(
user_w.as_ptr(),
domain_w.as_ptr(),
password_w.as_ptr(),
LOGON_WITH_PROFILE,
exe_w.as_ptr(),
cmdline_vec.as_mut_ptr(),
windows_sys::Win32::System::Threading::CREATE_NO_WINDOW
| windows_sys::Win32::System::Threading::CREATE_UNICODE_ENVIRONMENT,
env_block
.as_ref()
.map(|b| b.as_ptr() as *const c_void)
.unwrap_or(ptr::null()),
cwd_w.as_ptr(),
&si,
&mut pi,
)
};
if spawn_res == 0 {
let mut launch_attempt = 1;
loop {
let spawn_res = unsafe {
CreateProcessWithLogonW(
user_w.as_ptr(),
domain_w.as_ptr(),
password_w.as_ptr(),
LOGON_WITH_PROFILE,
exe_w.as_ptr(),
cmdline_vec.as_mut_ptr(),
windows_sys::Win32::System::Threading::CREATE_NO_WINDOW
| windows_sys::Win32::System::Threading::CREATE_UNICODE_ENVIRONMENT,
env_block
.as_ref()
.map(|b| b.as_ptr() as *const c_void)
.unwrap_or(ptr::null()),
cwd_w.as_ptr(),
&si,
&mut pi,
)
};
if spawn_res != 0 {
break;
}
let err = unsafe { GetLastError() } as i32;
log_note(
&format!(
"runner launch failed before process start: exe={} cmdline={} error={err}",
"runner launch failed before process start: exe={} cmdline={} error={err} attempt={launch_attempt}",
runner_exe.display(),
runner_full_cmd
),
logs_base_dir,
);
return Err(anyhow::anyhow!("CreateProcessWithLogonW failed: {err}"));
if launch_attempt > 1 || !should_retry_after_logon_failure(err) {
return Err(anyhow::anyhow!("CreateProcessWithLogonW failed: {err}"));
}
log_note(
&format!(
"runner launch hit recoverable logon error {err}; reprovisioning sandbox users and retrying"
),
logs_base_dir,
);
run_elevated_setup(
SandboxSetupRequest {
policy: &policy,
policy_cwd: sandbox_policy_cwd,
command_cwd: cwd,
env_map: &env_map,
codex_home,
proxy_enforced,
},
SetupRootOverrides {
read_roots: read_roots_override.map(<[PathBuf]>::to_vec),
write_roots: write_roots_override.map(<[PathBuf]>::to_vec),
deny_write_paths: Some(deny_write_paths_override.to_vec()),
},
)?;
sandbox_creds = require_logon_sandbox_creds(
&policy,
sandbox_policy_cwd,
cwd,
&env_map,
codex_home,
read_roots_override,
write_roots_override,
deny_write_paths_override,
proxy_enforced,
)?;
user_w = to_wide(&sandbox_creds.username);
password_w = to_wide(&sandbox_creds.password);
cmdline_vec = to_wide(&runner_full_cmd);
launch_attempt += 1;
}
if let Err(err) = connect_pipe(h_pipe_in) {
@@ -511,6 +565,13 @@ mod windows_impl {
fn applies_network_block_for_read_only() {
assert!(!SandboxPolicy::new_read_only_policy().has_full_network_access());
}
#[test]
fn retries_only_known_logon_errors() {
assert!(should_retry_after_logon_failure(1326));
assert!(should_retry_after_logon_failure(1909));
assert!(!should_retry_after_logon_failure(5));
}
}
}