Reuse private desktops across Windows sandbox commands (#40672)

## Why

Private desktops need to remain available across command-runner exits and idle
gaps.

## What changed

- Keep parent-owned private desktops alive and reuse them only when the sandbox
  account and effective permissions match.
- Pass the selected desktop name to elevated command runners, which validate and
  open the existing desktop instead of creating their own.
- Apply the same policy-aware reuse to the legacy Windows sandbox path.

## Testing

Add Windows tests for desktop-name validation, lifetime and concurrent reuse,
and separation when effective permissions differ.

GitOrigin-RevId: 2df8b1c31ff22d72549f2ec3065cd80ea1dd0c6a
This commit is contained in:
chess authored and copyberry committed 2026-08-25 18:41:03 +00:00
1 parent 731d969d03
commit 7e1ee6df6c
16 files changed
+642 -33

No files matched your search

@@ -3,6 +3,7 @@ use super::windows_common::make_runner_resizer;
use super::windows_common::start_runner_pipe_writer;
use super::windows_common::start_runner_stdin_writer;
use super::windows_common::start_runner_stdout_reader;
use crate::desktop::DesktopPolicy;
use crate::identity::SandboxCreds;
use crate::identity::refresh_logon_sandbox_creds;
use crate::ipc_framed::EmptyPayload;
@@ -96,10 +97,47 @@ async fn spawn_runner_transport_task(
request: RunnerTransportRequest,
) -> Result<RunnerTransport> {
tokio::task::spawn_blocking(move || -> Result<_> {
let desktop_policy = request
.spawn_request
.use_private_desktop
.then(|| {
DesktopPolicy::elevated(
crate::setup::SandboxSetupRequest {
permissions: &request.permissions,
command_cwd: &request.cwd,
env_map: &request.env_map,
codex_home: &request.codex_home,
proxy_enforced: request.proxy_enforced,
},
crate::setup::SetupRootOverrides {
read_roots: request.read_roots_override.clone(),
read_roots_include_platform_defaults: request
.read_roots_include_platform_defaults,
write_roots: request.write_roots_override.clone(),
deny_read_paths: Some(request.deny_read_paths_override.clone()),
deny_write_paths: Some(request.deny_write_paths_override.clone()),
},
&request.spawn_request.cap_sids,
request
.spawn_request
.network_proxy_restricting_sid
.as_deref(),
)
})
.transpose()?;
spawn_runner_transport_with_retry(
sandbox_creds,
&request,
spawn_runner_transport,
|codex_home, cwd, sandbox_creds, log_dir, spawn_request| {
spawn_runner_transport(
codex_home,
cwd,
sandbox_creds,
log_dir,
spawn_request,
desktop_policy.as_ref(),
)
},
refresh_logon_sandbox_creds,
)
})
@@ -177,6 +215,7 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated_for_permission_profil
tty,
stdin_open,
use_private_desktop,
private_desktop_name: None,
},
read_roots_override: read_roots_override.map(<[PathBuf]>::to_vec),
read_roots_include_platform_defaults,
@@ -81,6 +81,7 @@ fn retry_uses_original_unified_exec_request_and_stops_after_second_failure() {
tty: true,
stdin_open: true,
use_private_desktop: true,
private_desktop_name: None,
},
read_roots_override: Some(vec![PathBuf::from(r"C:\workspace\read")]),
read_roots_include_platform_defaults: true,
@@ -10,7 +10,9 @@ use crate::process::StderrMode;
use crate::process::StdinMode;
use crate::process::read_handle_loop;
use crate::process::spawn_process_with_pipes;
use crate::resolved_permissions::ResolvedWindowsSandboxPermissions;
use crate::spawn_prep::LegacyAclSids;
use crate::spawn_prep::LegacySessionSecurity;
use crate::spawn_prep::SpawnPrepOptions;
use crate::spawn_prep::allow_null_device_for_workspace_write;
use crate::spawn_prep::apply_legacy_session_acl_rules;
@@ -61,7 +63,9 @@ struct LegacyProcessHandles {
#[allow(clippy::too_many_arguments)]
fn spawn_legacy_process(
h_token: HANDLE,
security: &LegacySessionSecurity,
permissions: &ResolvedWindowsSandboxPermissions,
additional_deny_write_paths: &[std::path::PathBuf],
command: &[String],
cwd: &Path,
env_map: &HashMap<String, String>,
@@ -73,15 +77,19 @@ fn spawn_legacy_process(
writer_rx: mpsc::Receiver<Vec<u8>>,
logs_base_dir: Option<&Path>,
) -> Result<LegacyProcessHandles> {
let h_token = security.h_token;
let launch_desktop = LaunchDesktop::prepare_legacy(
use_private_desktop,
permissions,
cwd,
env_map,
security,
additional_deny_write_paths,
logs_base_dir,
)?;
let (pi, job, output_join, writer_handle, hpc, conpty_owner, desktop) = if tty {
let (pi, mut conpty) = spawn_conpty_process_as_user(
h_token,
command,
cwd,
env_map,
use_private_desktop,
logs_base_dir,
)?;
let (pi, mut conpty) =
spawn_conpty_process_as_user(h_token, command, cwd, env_map, launch_desktop)?;
let job = conpty
.job()
.ok_or_else(|| anyhow::anyhow!("spawned ConPTY is missing its process job"))?;
@@ -106,7 +114,7 @@ fn spawn_legacy_process(
},
StderrMode::Separate,
ConsoleMode::Inherit,
use_private_desktop,
launch_desktop,
logs_base_dir,
)?;
let stdout_join = spawn_output_reader(pipe_handles.stdout_read, stdout_tx);
@@ -389,7 +397,9 @@ pub(crate) async fn spawn_windows_sandbox_session_legacy(
token_handle,
desktop,
} = match spawn_legacy_process(
security.h_token,
&security,
&common.permissions,
&additional_deny_write_paths,
&command,
cwd,
&env_map,