windows-sandbox: retry 1312 launches on default desktop

This commit is contained in:
David Wiesen
2026-06-18 10:28:04 -07:00
parent 47ab51470b
commit fb8e07ea72
2 changed files with 142 additions and 84 deletions

View File

@@ -7,6 +7,7 @@
//! Windows sandbox flows that need a PTY.
use crate::desktop::LaunchDesktop;
use crate::logging;
use crate::proc_thread_attr::ProcThreadAttributeList;
use crate::winutil::format_last_error;
use crate::winutil::quote_windows_arg;
@@ -19,6 +20,7 @@ use std::ffi::c_void;
use std::os::windows::io::IntoRawHandle;
use std::path::Path;
use windows_sys::Win32::Foundation::CloseHandle;
use windows_sys::Win32::Foundation::ERROR_NO_SUCH_LOGON_SESSION;
use windows_sys::Win32::Foundation::GetLastError;
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE;
@@ -111,40 +113,66 @@ pub fn spawn_conpty_process_as_user(
si.StartupInfo.hStdInput = INVALID_HANDLE_VALUE;
si.StartupInfo.hStdOutput = INVALID_HANDLE_VALUE;
si.StartupInfo.hStdError = INVALID_HANDLE_VALUE;
let desktop = LaunchDesktop::prepare(use_private_desktop, logs_base_dir)?;
si.StartupInfo.lpDesktop = desktop.startup_info_desktop();
let raw = RawConPty::new(/*cols*/ 80, /*rows*/ 24)?;
let (pseudoconsole, input_write, output_read) = raw.into_handles();
let hpc = pseudoconsole.raw_handle() as HANDLE;
let conpty = ConptyInstance {
pseudoconsole: Some(pseudoconsole),
input_write: input_write.into_raw_handle() as HANDLE,
output_read: output_read.into_raw_handle() as HANDLE,
_desktop: Some(desktop),
};
let input_write = input_write.into_raw_handle() as HANDLE;
let output_read = output_read.into_raw_handle() as HANDLE;
let mut attrs = ProcThreadAttributeList::new(/*attr_count*/ 1)?;
attrs.set_pseudoconsole(hpc)?;
si.lpAttributeList = attrs.as_mut_ptr();
let mut pi: PROCESS_INFORMATION = unsafe { std::mem::zeroed() };
let ok = unsafe {
CreateProcessAsUserW(
h_token,
std::ptr::null(),
cmdline.as_mut_ptr(),
std::ptr::null_mut(),
std::ptr::null_mut(),
0,
EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT,
env_block.as_ptr() as *mut c_void,
to_wide(cwd).as_ptr(),
&si.StartupInfo,
&mut pi,
)
};
if ok == 0 {
let mut attempt_private_desktop = use_private_desktop;
loop {
let desktop = LaunchDesktop::prepare(attempt_private_desktop, logs_base_dir)?;
si.StartupInfo.lpDesktop = desktop.startup_info_desktop();
let mut pi: PROCESS_INFORMATION = unsafe { std::mem::zeroed() };
let ok = unsafe {
CreateProcessAsUserW(
h_token,
std::ptr::null(),
cmdline.as_mut_ptr(),
std::ptr::null_mut(),
std::ptr::null_mut(),
0,
EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT,
env_block.as_ptr() as *mut c_void,
to_wide(cwd).as_ptr(),
&si.StartupInfo,
&mut pi,
)
};
if ok != 0 {
let conpty = ConptyInstance {
pseudoconsole: Some(pseudoconsole),
input_write,
output_read,
_desktop: Some(desktop),
};
return Ok((pi, conpty));
}
let err = unsafe { GetLastError() } as i32;
logging::debug_log(
&format!(
"CreateProcessAsUserW failed: {} ({}) | cwd={} | cmd={} | env_u16_len={}",
err,
format_last_error(err),
cwd.display(),
cmdline_str,
env_block.len()
),
logs_base_dir,
);
if attempt_private_desktop && err == ERROR_NO_SUCH_LOGON_SESSION as i32 {
logging::debug_log(
"CreateProcessAsUserW returned ERROR_NO_SUCH_LOGON_SESSION on a private desktop; retrying PTY launch on Winsta0\\Default",
logs_base_dir,
);
attempt_private_desktop = false;
continue;
}
return Err(anyhow::anyhow!(
"CreateProcessAsUserW failed: {} ({}) | cwd={} | cmd={} | env_u16_len={}",
err,
@@ -154,5 +182,4 @@ pub fn spawn_conpty_process_as_user(
env_block.len()
));
}
Ok((pi, conpty))
}

View File

@@ -11,6 +11,7 @@ use std::ffi::c_void;
use std::path::Path;
use std::ptr;
use windows_sys::Win32::Foundation::CloseHandle;
use windows_sys::Win32::Foundation::ERROR_NO_SUCH_LOGON_SESSION;
use windows_sys::Win32::Foundation::GetLastError;
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::Foundation::HANDLE_FLAG_INHERIT;
@@ -36,6 +37,10 @@ pub struct CreatedProcess {
_desktop: LaunchDesktop,
}
fn should_retry_on_default_desktop(err: i32, use_private_desktop: bool) -> bool {
use_private_desktop && err == ERROR_NO_SUCH_LOGON_SESSION as i32
}
pub fn make_env_block(env: &HashMap<String, String>) -> Vec<u16> {
let mut items: Vec<(String, String)> =
env.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
@@ -87,22 +92,11 @@ pub unsafe fn create_process_as_user(
let cmdline_str = argv_to_command_line(argv);
let mut cmdline: Vec<u16> = to_wide(&cmdline_str);
let env_block = make_env_block(env_map);
let desktop = LaunchDesktop::prepare(use_private_desktop, logs_base_dir)?;
let mut pi: PROCESS_INFORMATION = std::mem::zeroed();
let cwd_wide = to_wide(cwd);
let env_block_len = env_block.len();
match stdio {
Some((stdin_h, stdout_h, stderr_h)) => {
let mut si: STARTUPINFOEXW = std::mem::zeroed();
si.StartupInfo.cb = std::mem::size_of::<STARTUPINFOEXW>() as u32;
// Some processes (e.g., PowerShell) can fail with STATUS_DLL_INIT_FAILED
// if lpDesktop is not set when launching with a restricted token.
// Point explicitly at the interactive desktop or a private desktop.
si.StartupInfo.lpDesktop = desktop.startup_info_desktop();
si.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
si.StartupInfo.hStdInput = stdin_h;
si.StartupInfo.hStdOutput = stdout_h;
si.StartupInfo.hStdError = stderr_h;
let mut inherited_handles = vec![stdin_h, stdout_h];
if !inherited_handles.contains(&stderr_h) {
inherited_handles.push(stderr_h);
@@ -115,25 +109,46 @@ pub unsafe fn create_process_as_user(
));
}
}
let mut attrs = ProcThreadAttributeList::new(/*attr_count*/ 1)?;
attrs.set_handle_list(inherited_handles)?;
si.lpAttributeList = attrs.as_mut_ptr();
let creation_flags = CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT;
let ok = CreateProcessAsUserW(
h_token,
std::ptr::null(),
cmdline.as_mut_ptr(),
std::ptr::null_mut(),
std::ptr::null_mut(),
1,
creation_flags,
env_block.as_ptr() as *mut c_void,
cwd_wide.as_ptr(),
&si.StartupInfo,
&mut pi,
);
if ok == 0 {
let mut attempt_private_desktop = use_private_desktop;
loop {
let desktop = LaunchDesktop::prepare(attempt_private_desktop, logs_base_dir)?;
let mut si: STARTUPINFOEXW = std::mem::zeroed();
si.StartupInfo.cb = std::mem::size_of::<STARTUPINFOEXW>() as u32;
// Some processes (e.g., PowerShell) can fail with STATUS_DLL_INIT_FAILED
// if lpDesktop is not set when launching with a restricted token.
// Point explicitly at the interactive desktop or a private desktop.
si.StartupInfo.lpDesktop = desktop.startup_info_desktop();
si.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
si.StartupInfo.hStdInput = stdin_h;
si.StartupInfo.hStdOutput = stdout_h;
si.StartupInfo.hStdError = stderr_h;
let mut attrs = ProcThreadAttributeList::new(/*attr_count*/ 1)?;
attrs.set_handle_list(inherited_handles.clone())?;
si.lpAttributeList = attrs.as_mut_ptr();
let ok = CreateProcessAsUserW(
h_token,
std::ptr::null(),
cmdline.as_mut_ptr(),
std::ptr::null_mut(),
std::ptr::null_mut(),
1,
creation_flags,
env_block.as_ptr() as *mut c_void,
cwd_wide.as_ptr(),
&si.StartupInfo,
&mut pi,
);
if ok != 0 {
return Ok(CreatedProcess {
process_info: pi,
startup_info: si.StartupInfo,
_desktop: desktop,
});
}
let err = GetLastError() as i32;
let msg = format!(
"CreateProcessAsUserW failed: {} ({}) | cwd={} | cmd={} | env_u16_len={} | si_flags={} | creation_flags={}",
@@ -146,35 +161,48 @@ pub unsafe fn create_process_as_user(
creation_flags,
);
logging::debug_log(&msg, logs_base_dir);
if should_retry_on_default_desktop(err, attempt_private_desktop) {
logging::debug_log(
"CreateProcessAsUserW returned ERROR_NO_SUCH_LOGON_SESSION on a private desktop; retrying on Winsta0\\Default",
logs_base_dir,
);
attempt_private_desktop = false;
continue;
}
return Err(anyhow!("CreateProcessAsUserW failed: {err}"));
}
Ok(CreatedProcess {
process_info: pi,
startup_info: si.StartupInfo,
_desktop: desktop,
})
}
None => {
let mut si: STARTUPINFOW = std::mem::zeroed();
si.cb = std::mem::size_of::<STARTUPINFOW>() as u32;
si.lpDesktop = desktop.startup_info_desktop();
ensure_inheritable_stdio(&mut si)?;
let creation_flags = CREATE_UNICODE_ENVIRONMENT;
let ok = CreateProcessAsUserW(
h_token,
std::ptr::null(),
cmdline.as_mut_ptr(),
std::ptr::null_mut(),
std::ptr::null_mut(),
1,
creation_flags,
env_block.as_ptr() as *mut c_void,
cwd_wide.as_ptr(),
&si,
&mut pi,
);
if ok == 0 {
let mut attempt_private_desktop = use_private_desktop;
loop {
let desktop = LaunchDesktop::prepare(attempt_private_desktop, logs_base_dir)?;
let mut si: STARTUPINFOW = std::mem::zeroed();
si.cb = std::mem::size_of::<STARTUPINFOW>() as u32;
si.lpDesktop = desktop.startup_info_desktop();
ensure_inheritable_stdio(&mut si)?;
let ok = CreateProcessAsUserW(
h_token,
std::ptr::null(),
cmdline.as_mut_ptr(),
std::ptr::null_mut(),
std::ptr::null_mut(),
1,
creation_flags,
env_block.as_ptr() as *mut c_void,
cwd_wide.as_ptr(),
&si,
&mut pi,
);
if ok != 0 {
return Ok(CreatedProcess {
process_info: pi,
startup_info: si,
_desktop: desktop,
});
}
let err = GetLastError() as i32;
let msg = format!(
"CreateProcessAsUserW failed: {} ({}) | cwd={} | cmd={} | env_u16_len={} | si_flags={} | creation_flags={}",
@@ -187,13 +215,16 @@ pub unsafe fn create_process_as_user(
creation_flags,
);
logging::debug_log(&msg, logs_base_dir);
if should_retry_on_default_desktop(err, attempt_private_desktop) {
logging::debug_log(
"CreateProcessAsUserW returned ERROR_NO_SUCH_LOGON_SESSION on a private desktop; retrying on Winsta0\\Default",
logs_base_dir,
);
attempt_private_desktop = false;
continue;
}
return Err(anyhow!("CreateProcessAsUserW failed: {err}"));
}
Ok(CreatedProcess {
process_info: pi,
startup_info: si,
_desktop: desktop,
})
}
}
}