Files
codex/codex-rs/windows-sandbox-rs/src/proc_thread_attr.rs
T
iceweasel-oaiandGitHub 5cac3f896d Fix Windows pseudoconsole attribute handling for sandboxed PTY sessions (#20042)
## 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
2026-04-29 11:59:45 +02:00

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());
}
}
}