mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
## Summary Fix the Windows sandbox PTY spawn path to pass the pseudoconsole handle value directly into `UpdateProcThreadAttribute`. ## Why Sandboxed `unified_exec` PTY sessions on Windows were failing during child process startup with `0xc0000142` (`STATUS_DLL_INIT_FAILED`). In practice this showed up as PowerShell DLL init popups when the sandboxed background-terminal path tried to launch an interactive shell. The root cause was that we were passing a pointer to a local `isize` variable instead of the pseudoconsole handle value in the form Windows expects for `PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE`. ## Validation - `cargo build -p codex-windows-sandbox --bins` - Reproduced the real sandboxed `codex exec` flow with `windows.sandbox_private_desktop=true` - Verified a `tty=true` interactive session launched through the normal PowerShell wrapper, printed `READY`, accepted follow-up stdin, and exited cleanly - Confirmed no new `0xc0000142` / `Application Popup` events appeared after the successful repro
101 lines
3.2 KiB
Rust
101 lines
3.2 KiB
Rust
use std::ffi::c_void;
|
|
use std::io;
|
|
use windows_sys::Win32::Foundation::GetLastError;
|
|
use windows_sys::Win32::Foundation::HANDLE;
|
|
use windows_sys::Win32::System::Threading::DeleteProcThreadAttributeList;
|
|
use windows_sys::Win32::System::Threading::InitializeProcThreadAttributeList;
|
|
use windows_sys::Win32::System::Threading::LPPROC_THREAD_ATTRIBUTE_LIST;
|
|
use windows_sys::Win32::System::Threading::UpdateProcThreadAttribute;
|
|
|
|
const PROC_THREAD_ATTRIBUTE_HANDLE_LIST: usize = 0x0002_0002;
|
|
const PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE: usize = 0x0002_0016;
|
|
|
|
pub struct ProcThreadAttributeList {
|
|
buffer: Vec<u8>,
|
|
handle_list: Option<Vec<HANDLE>>,
|
|
}
|
|
|
|
impl ProcThreadAttributeList {
|
|
pub fn new(attr_count: u32) -> io::Result<Self> {
|
|
let mut size: usize = 0;
|
|
unsafe {
|
|
InitializeProcThreadAttributeList(std::ptr::null_mut(), attr_count, 0, &mut size);
|
|
}
|
|
if size == 0 {
|
|
return Err(io::Error::from_raw_os_error(unsafe {
|
|
GetLastError() as i32
|
|
}));
|
|
}
|
|
let mut buffer = vec![0u8; size];
|
|
let list = buffer.as_mut_ptr() as LPPROC_THREAD_ATTRIBUTE_LIST;
|
|
let ok = unsafe { InitializeProcThreadAttributeList(list, attr_count, 0, &mut size) };
|
|
if ok == 0 {
|
|
return Err(io::Error::from_raw_os_error(unsafe {
|
|
GetLastError() as i32
|
|
}));
|
|
}
|
|
Ok(Self {
|
|
buffer,
|
|
handle_list: None,
|
|
})
|
|
}
|
|
|
|
pub fn as_mut_ptr(&mut self) -> LPPROC_THREAD_ATTRIBUTE_LIST {
|
|
self.buffer.as_mut_ptr() as LPPROC_THREAD_ATTRIBUTE_LIST
|
|
}
|
|
|
|
pub fn set_pseudoconsole(&mut self, hpc: isize) -> io::Result<()> {
|
|
let list = self.as_mut_ptr();
|
|
let ok = unsafe {
|
|
UpdateProcThreadAttribute(
|
|
list,
|
|
0,
|
|
PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
|
|
hpc as *mut c_void,
|
|
std::mem::size_of::<HANDLE>(),
|
|
std::ptr::null_mut(),
|
|
std::ptr::null_mut(),
|
|
)
|
|
};
|
|
if ok == 0 {
|
|
return Err(io::Error::from_raw_os_error(unsafe {
|
|
GetLastError() as i32
|
|
}));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn set_handle_list(&mut self, handles: Vec<HANDLE>) -> io::Result<()> {
|
|
self.handle_list = Some(handles);
|
|
let list = self.as_mut_ptr();
|
|
let Some(handle_list) = self.handle_list.as_mut() else {
|
|
return Err(io::Error::other("handle list missing after initialization"));
|
|
};
|
|
let ok = unsafe {
|
|
UpdateProcThreadAttribute(
|
|
list,
|
|
0,
|
|
PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
|
|
handle_list.as_mut_ptr().cast(),
|
|
std::mem::size_of_val(handle_list.as_slice()),
|
|
std::ptr::null_mut(),
|
|
std::ptr::null_mut(),
|
|
)
|
|
};
|
|
if ok == 0 {
|
|
return Err(io::Error::from_raw_os_error(unsafe {
|
|
GetLastError() as i32
|
|
}));
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Drop for ProcThreadAttributeList {
|
|
fn drop(&mut self) {
|
|
unsafe {
|
|
DeleteProcThreadAttributeList(self.as_mut_ptr());
|
|
}
|
|
}
|
|
}
|