{"record":{"id":"cc212cc451999bf3","repo":"openai/codex","slug":"failed-to-place-bubblewrap-child-in-its-own-proces","errorCode":null,"errorMessage":"failed to place bubblewrap child in its own process group: {err}","messagePattern":"failed to place bubblewrap child in its own process group: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"codex-rs/linux-sandbox/src/linux_run_main.rs","lineNumber":593,"sourceCode":"    let setup_signal_mask = ForwardedSignalMask::block();\n    let synthetic_mount_registrations = register_synthetic_mount_targets(&synthetic_mount_targets);\n    let protected_create_registrations =\n        register_protected_create_targets(&protected_create_targets);\n    let exec_start_pipe = create_exec_start_pipe(!protected_create_targets.is_empty());\n    let parent_pid = unsafe { libc::getpid() };\n    let pid = unsafe { libc::fork() };\n    if pid < 0 {\n        let err = std::io::Error::last_os_error();\n        panic!(\"failed to fork for bubblewrap: {err}\");\n    }\n\n    if pid == 0 {\n        reset_forwarded_signal_handlers_to_default();\n        setup_signal_mask.restore();\n        let setpgid_res = unsafe { libc::setpgid(0, 0) };\n        if setpgid_res < 0 {\n            let err = std::io::Error::last_os_error();\n            panic!(\"failed to place bubblewrap child in its own process group: {err}\");\n        }\n        terminate_with_parent(parent_pid);\n        wait_for_parent_exec_start(exec_start_pipe[0], exec_start_pipe[1]);\n        exec_bwrap(args, preserved_files);\n    }\n\n    close_child_exec_start_read(exec_start_pipe[0]);\n    let protected_create_monitor = ProtectedCreateMonitor::start(&protected_create_targets);\n    let signal_forwarders = install_bwrap_signal_forwarders(pid);\n    release_child_exec_start(exec_start_pipe[1]);\n    setup_signal_mask.restore();\n    let status = wait_for_bwrap_child(pid);\n    let cleanup_signal_mask = ForwardedSignalMask::block();\n    BWRAP_CHILD_PID.store(0, Ordering::SeqCst);\n    let protected_create_monitor_violation = protected_create_monitor\n        .map(ProtectedCreateMonitor::stop)\n        .unwrap_or(false);\n    cleanup_synthetic_mount_targets(&synthetic_mount_registrations);","sourceCodeStart":575,"sourceCodeEnd":611,"githubUrl":"https://github.com/openai/codex/blob/339751715c64496cb86246bfb3935f40e309dd3d/codex-rs/linux-sandbox/src/linux_run_main.rs#L575-L611","documentation":"The forked child that will exec bubblewrap calls libc::setpgid(0, 0) to move itself into a fresh process group; the parent relies on this to forward signals to the whole sandbox tree via kill(-pid, ...) (send_signal_to_bwrap_child, linux_run_main.rs:912). Linux guarantees setpgid(0,0) succeeds on a just-forked child, so this panic means the environment broke that invariant: an outer seccomp/LSM policy denies setpgid, or an exotic runtime (gVisor, a nested sandbox) restricts process-group changes. The child dies before exec, so every sandboxed run fails.","triggerScenarios":"Any sandboxed command whose BwrapArgs carry synthetic mount targets or protected-create targets, forcing the fork path in run_or_exec_bwrap (linux_run_main.rs:568-598), executed under a wrapper whose seccomp filter blocks setpgid (EPERM) or denies it through policy.","commonSituations":"Nesting the linux-sandbox launcher inside another sandbox or container with a restrictive seccomp profile; hardened CI runners; gVisor-based runtimes; kernel hardening modules denying process-group manipulation.","solutions":["Stop nesting sandboxes, or run the launcher outside the restrictive wrapper.","If you own the outer seccomp policy, allow the setpgid syscall (syscall 109, @process group).","Reproduce with strace -f -e setpgid to capture the exact errno printed in the panic.","If the errno is unexpected on a stock kernel, report it against codex-rs/linux-sandbox with the trace."],"exampleFix":"# before: outer unit filters process-management calls\nSystemCallFilter=~@process\n\n# after: allow setpgid for the nested sandbox launcher\nSystemCallFilter=add setpgid","handlingStrategy":"validation","validationCode":"fn process_group_creation_works() -> bool {\n    match unsafe { libc::fork() } {\n        0 => unsafe { libc::_exit(if libc::setpgid(0, 0) == 0 { 0 } else { 1 }) },\n        pid if pid > 0 => {\n            let mut status = 0;\n            unsafe { libc::waitpid(pid, &mut status, 0) };\n            libc::WIFEXITED(status) && libc::WEXITSTATUS(status) == 0\n        }\n        _ => false,\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Do not run the linux sandbox launcher under a seccomp profile that denies setpgid.","Add a CI smoke test that executes one trivial sandboxed command to catch environment regressions early.","Record the errno from the panic message to identify which policy layer denied the syscall."],"tags":["sandbox","bubblewrap","setpgid","process-group","seccomp","fork","linux"],"backgroundTag":"setpgid-permission-denied","analyzedSha":"339751715c64496cb86246bfb3935f40e309dd3d","analyzedAt":"2026-08-25T05:35:09.876Z","schemaVersion":2},"datasetVersion":"2026-08-25T06:17:31.827Z"}