Add Windows sandbox unified exec runtime support#15578
Conversation
|
@codex review |
There was a problem hiding this comment.
💡 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".
|
@codex review |
There was a problem hiding this comment.
💡 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".
|
@codex review |
There was a problem hiding this comment.
💡 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".
|
@codex review |
There was a problem hiding this comment.
💡 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) } { |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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 👍 / 👎.
jif-oai
left a comment
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
this seems a bit odd to only load, and potentially error, at the call site of the tool
This should have been done upfront
| stdin_open, | ||
| use_private_desktop, | ||
| }; | ||
| send_spawn_request(&mut pipe_write, spawn_request)?; |
There was a problem hiding this comment.
We should bound the spawn_ready handshake. We don't want infinite hang in case of any handshake issue
| 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) }; |
There was a problem hiding this comment.
Maybe I'm wrong but shouldn't we revert this later?
jif-oai
left a comment
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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() }, |
There was a problem hiding this comment.
I think you have another leak here
| &command, | ||
| )?; | ||
|
|
||
| let mut transport = spawn_runner_transport( |
There was a problem hiding this comment.
this is blocking.. this can stale a tokio worker
|
|
||
| match msg.message { | ||
| Message::Output { payload } => { | ||
| if let Ok(data) = decode_bytes(&payload.data_b64) { |
There was a problem hiding this comment.
we should log the potential decode issues...
| @@ -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| { | |||
There was a problem hiding this comment.
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 { | |||
There was a problem hiding this comment.
ultra-nit / non-blocking - macro for this?
7bc7db8 to
1ec4660
Compare
1ec4660 to
8b36fb4
Compare
Co-authored-by: Codex <noreply@openai.com>
jif-oai
left a comment
There was a problem hiding this comment.
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)?; |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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), |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
What if not ok ? Are we 100% sure this will never happen?
Summary
This is the runtime/foundation half of the Windows sandbox unified-exec work.
unified_execsession support inwindows-sandbox-rsfor both:codex-windows-sandbox/codex-utils-ptyThis PR does not enable Windows sandbox
UnifiedExecfor 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 toShellCommand.This PR isolates the runtime/session layer so it can be reviewed independently from product-surface enablement.