Skip to content

Add Windows sandbox unified exec runtime support#15578

Merged
iceweasel-oai merged 23 commits into
mainfrom
windows-sandbox-unified-exec-sessions
Apr 21, 2026
Merged

Add Windows sandbox unified exec runtime support#15578
iceweasel-oai merged 23 commits into
mainfrom
windows-sandbox-unified-exec-sessions

Conversation

@iceweasel-oai
Copy link
Copy Markdown
Collaborator

@iceweasel-oai iceweasel-oai commented Mar 23, 2026

Summary

This is the runtime/foundation half of the Windows sandbox unified-exec work.

  • add Windows sandbox unified_exec session support in windows-sandbox-rs for both:
    • the legacy restricted-token backend
    • the elevated runner backend
  • extend the PTY/process runtime so driver-backed sessions can support:
    • stdin streaming
    • stdout/stderr separation
    • exit propagation
    • PTY resize hooks
  • add Windows sandbox runtime coverage in codex-windows-sandbox / codex-utils-pty

This PR does not enable Windows sandbox UnifiedExec for product callers yet because hooking this up to app-server comes in the next PR.

Windows sandbox advertising is intentionally kept aligned with main, so sandboxed Windows callers still fall back to ShellCommand.

This PR isolates the runtime/session layer so it can be reviewed independently from product-surface enablement.

@iceweasel-oai
Copy link
Copy Markdown
Collaborator Author

@codex review

Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 969be7c0ad

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread codex-rs/windows-sandbox-rs/src/unified_exec/session.rs Outdated
Comment thread codex-rs/windows-sandbox-rs/src/spawn_prep.rs
Comment thread codex-rs/windows-sandbox-rs/src/winutil.rs Outdated
@iceweasel-oai
Copy link
Copy Markdown
Collaborator Author

@codex review

Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 48bab9be43

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread codex-rs/windows-sandbox-rs/src/unified_exec/session.rs Outdated
Comment thread codex-rs/windows-sandbox-rs/src/unified_exec/session.rs Outdated
@iceweasel-oai
Copy link
Copy Markdown
Collaborator Author

@codex review

Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4d486e3c5c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread codex-rs/windows-sandbox-rs/src/winutil.rs Outdated
Comment thread codex-rs/windows-sandbox-rs/src/unified_exec/tests.rs Outdated
@iceweasel-oai
Copy link
Copy Markdown
Collaborator Author

@codex review

Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cd873f727d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


if !guards.is_empty() {
if let Some(cap_sid) = cap_sid {
if let Some(sid) = unsafe { convert_string_sid_to_sid(&cap_sid) } {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Free SID returned by ConvertStringSidToSidW

convert_string_sid_to_sid allocates SID memory that must be released with LocalFree (see token.rs). In finalize_exit, the SID created for ACE revocation is never freed, so every read-only session leaks heap memory. Long-lived agents that open many sessions will accumulate unreclaimed allocations.

Useful? React with 👍 / 👎.

) = unsafe {
match policy {
SandboxPolicy::ReadOnly { .. } => {
let psid = convert_string_sid_to_sid(&caps.readonly).unwrap();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Release legacy session SID pointers after use

prepare_legacy_session_security stores raw SID pointers from convert_string_sid_to_sid in LegacySessionSecurity, but there is no corresponding LocalFree on success or cleanup paths. Each spawned legacy session leaks one or more SID allocations, causing unbounded memory growth over many sessions.

Useful? React with 👍 / 👎.

@iceweasel-oai iceweasel-oai marked this pull request as ready for review March 24, 2026 18:41
Copy link
Copy Markdown
Collaborator

@jif-oai jif-oai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we transfer a bit too much of the windows specificities from the pty lib to the unified_exec implementation. IMO this will reduce maintainability over time

) -> Result<UnifiedExecProcess, UnifiedExecError> {
let (program, args) = env
.command
.split_first()
.ok_or(UnifiedExecError::MissingCommandLine)?;
let inherited_fds = spawn_lifecycle.inherited_fds();

#[cfg(target_os = "windows")]
if env.sandbox == crate::exec::SandboxType::WindowsRestrictedToken {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems a bit odd to only load, and potentially error, at the call site of the tool
This should have been done upfront

Comment thread codex-rs/core/src/unified_exec/process_manager.rs Outdated
Comment thread codex-rs/utils/pty/src/win/psuedocon.rs
Comment thread codex-rs/utils/pty/src/process.rs
Comment thread codex-rs/utils/pty/src/process.rs Outdated
Comment thread codex-rs/windows-sandbox-rs/src/unified_exec/session.rs Outdated
Comment thread codex-rs/windows-sandbox-rs/src/unified_exec/session.rs Outdated
stdin_open,
use_private_desktop,
};
send_spawn_request(&mut pipe_write, spawn_request)?;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should bound the spawn_ready handshake. We don't want infinite hang in case of any handshake issue

Comment thread codex-rs/windows-sandbox-rs/src/unified_exec/session.rs Outdated
let user_w = to_wide(&sandbox_creds.username);
let domain_w = to_wide(".");
let password_w = to_wide(&sandbox_creds.password);
let _ = unsafe { SetErrorMode(0x0001 | 0x0002) };
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm wrong but shouldn't we revert this later?

@iceweasel-oai iceweasel-oai requested a review from jif-oai March 31, 2026 20:50
Copy link
Copy Markdown
Collaborator

@jif-oai jif-oai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think some previous comments have been addressed yet

Global, I'm a bit scared by the stability of this code and this PR is becoming gigantic. How do you plan to test it deeply before shipping this?

) = unsafe {
match policy {
SandboxPolicy::ReadOnly { .. } => {
let psid = convert_string_sid_to_sid(&caps.readonly).unwrap();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you have a leak here

let caps = load_or_create_cap_sids(codex_home)?;
let (psid_to_use, cap_sids) = match &common.policy {
SandboxPolicy::ReadOnly { .. } => (
unsafe { convert_string_sid_to_sid(&caps.readonly).unwrap() },
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you have another leak here

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sd is never freed

&command,
)?;

let mut transport = spawn_runner_transport(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is blocking.. this can stale a tokio worker


match msg.message {
Message::Output { payload } => {
if let Ok(data) = decode_bytes(&payload.data_b64) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should log the potential decode issues...

Comment thread codex-rs/core/src/sandboxing/mod.rs Outdated
@@ -549,6 +549,59 @@ impl UnifiedExecProcessManager {
.ok_or(UnifiedExecError::MissingCommandLine)?;
let inherited_fds = spawn_lifecycle.inherited_fds();

#[cfg(target_os = "windows")]
if env.sandbox == crate::exec::SandboxType::WindowsRestrictedToken {
let policy_json = serde_json::to_string(&env.sandbox_policy).map_err(|err| {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of scope for this PR, but would be great to start using PermissionProfile instead of a json serialization here.

@@ -205,6 +226,20 @@ impl Drop for ProcessHandle {
}
}

/// Adapts a closure into a `ChildTerminator` implementation.
struct ClosureTerminator {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ultra-nit / non-blocking - macro for this?

@iceweasel-oai iceweasel-oai force-pushed the windows-sandbox-unified-exec-sessions branch 2 times, most recently from 7bc7db8 to 1ec4660 Compare April 10, 2026 20:17
@iceweasel-oai iceweasel-oai force-pushed the windows-sandbox-unified-exec-sessions branch from 1ec4660 to 8b36fb4 Compare April 20, 2026 21:53
@iceweasel-oai iceweasel-oai requested a review from a team as a code owner April 20, 2026 21:53
Copy link
Copy Markdown
Collaborator

@jif-oai jif-oai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the happy path looks good but the failure path can still be racy
I approve as this is not for prod yet and it will be easier to review smaller PRs containing fixes and robustification

}

let connect_result = (|| -> Result<()> {
connect_pipe(h_pipe_in)?;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still unbounded no?
So if the runner dies before opening one of the named pipe, we will hang forever

})
}

pub(crate) fn start_runner_stdout_reader(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the runner stdout reader is detached and not owned by ProcessHandle... but the matching pipe writer thread is also detached so terminate() can't reliably reclaim a wedged elevanted session. This will create a leak

loop {
let recv_result = if process_exited {
match tokio::time::timeout(
std::time::Duration::from_millis(200),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my experience, some process can signal exist before the final flush of their STDOUT. So here if we have an exist before 200ms and the flush comes after, we will loose the tail

In general, those things are extremely racy so please ask Codex multiple time to look and identify every possible races. Process handling is a very difficult job


match msg.message {
Message::Output { payload } => {
if let Ok(data) = decode_bytes(&payload.data_b64) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if not ok ? Are we 100% sure this will never happen?

@iceweasel-oai iceweasel-oai merged commit 8612714 into main Apr 21, 2026
24 of 25 checks passed
@iceweasel-oai iceweasel-oai deleted the windows-sandbox-unified-exec-sessions branch April 21, 2026 17:44
@github-actions github-actions Bot locked and limited conversation to collaborators Apr 21, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

3 participants