openai / openai/codex

Linux desktop app.relaunch() sets NoNewPrivs on relaunch helper; inherited flag blocks sudo in full-access sessions

Open
#44,680 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

app bug Linux sandbox
Dominant language
Rust
Stars
125k
Forks
19.4k
PR merge metrics
PR metrics pending

Description

What version of the Codex App are you using?

Linux desktop Debian package chatgpt 26.903.71938, bundled codex-cli 0.153.4.
Native runtime process.versions.chrome and .electron both report 152.0.7977.83. The package's /usr/lib/chatgpt/version file says 42.3.0, so that file alone is not a reliable identification of this runtime.

What subscription do you have?

Not included; this reproducer does not require an account or model call.

What platform is your computer?

Debian, Linux 6.19.14+deb14-amd64 x86_64 GNU/Linux.

What issue are you seeing?

The Linux desktop main process has NoNewPrivs: 1, inherited by its Codex app-server and command shells, including a full-access session with sandbox disabled. sudo -n su -c id fails with:

sudo: The "no new privileges" flag is set, which prevents sudo from running as root.

The user reports passwordless sudo works in their ordinary desktop terminal. The same main-process PID remains running from an earlier app restart, so this investigation has not yet measured a fresh desktop launch from a NoNewPrivs: 0 terminal.

I dynamically verified a call that explicitly sets this restriction on the installed native runtime’s relaunch path: an isolated minimal app calling app.relaunch() makes the child execute prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) immediately before exec of the --type=relauncher helper. That helper launches the replacement main app, which inherits the irreversible restriction.

What steps can reproduce the bug?

Copy the native executable and its supporting runtime files into a disposable directory, keeping the installed app untouched. In a separate source directory, create package.json containing {"name":"nnp-relaunch-test","version":"1.0.0","main":"main.js"} and the following main.js; pack those two files as the disposable runtime’s resources/app.asar (e.g. with the Electron ASAR packaging tool). Use a separate --user-data-dir and export a disposable log path in NNP_TEST_LOG. Do not copy the real application bundle into the test runtime, replace the installed app’s resources, or restart an active work session:

const { app } = require('electron');
const fs = require('fs');
const phase = process.argv.includes('--nnp-phase-two')
  ? 'after-relaunch' : 'before-relaunch';
fs.appendFileSync(process.env.NNP_TEST_LOG, JSON.stringify({
  phase,
  pid: process.pid,
  ppid: process.ppid,
  status: fs.readFileSync('/proc/self/status', 'utf8')
    .match(/^NoNewPrivs:.*$/m)[0],
  versions: process.versions
}) + '\n');
if (phase === 'before-relaunch') {
  app.relaunch({ args: process.argv.slice(1).concat(['--nnp-phase-two']) });
}
app.exit(0);

Trace with strace -f -s 300 -e trace=prctl,execve,clone,fork,vfork -o trace.log /path/to/isolated/ChatGPT --user-data-dir=/path/to/disposable/profile.

Observed trace excerpt (paths shortened; runtime binary unchanged):

264427 prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) = 0
264427 execve(".../runtime/ChatGPT", [".../runtime/ChatGPT", "--type=relauncher", "--no-sandbox", "---", ".../runtime/ChatGPT", "--user-data-dir=.../profile", "--nnp-phase-two"], ...) = 0
264429 execve(".../runtime/ChatGPT", [".../runtime/ChatGPT", "--user-data-dir=.../profile", "--nnp-phase-two"], ...) = 0

The --no-sandbox helper argument is generated internally by app.relaunch(); it was not supplied to the initial app. No security flags or native binary code were modified.

Both phases reported NoNewPrivs: 1 because the diagnostic shell already inherited that flag. This is a direct trace of the setter on the relaunch path, not a claimed measurement of a 0→1 transition or proof of the historical syscall for the live desktop process. A maintainer should repeat from a normal NoNewPrivs: 0 shell and compare fresh start vs relaunch.

What is the expected behavior?

A normal desktop app restart should preserve the main app's existing privilege state, so an explicitly full-access Codex session does not unexpectedly lose the ability to use authorized sudo after restart. Sandboxed renderers and intentionally restricted command sessions should retain their security restrictions.

Additional information
  • Tested a copied native binary with SHA-256 identical to the installed executable: fda09eaccd809821e71b33275bd20cac20b8bd587f1756471fb705cd59e4542d.
  • This native runtime emits source paths under owl/browser/api/, so attributing it exclusively to stock Electron 42.3.0 would be inaccurate.
  • Inspected public openai/codex at 9e22e74e8dcab53f8bf1799c0eed1f9834c32f1a. The relevant native desktop relaunch implementation is not present there. The Rust Linux sandbox's deliberate NNP use should not be removed to treat an inherited parent-process flag; Linux cannot clear an inherited NNP flag.
  • Please route to the Linux desktop/Owl runtime relaunch owner. A narrow fix should preserve the parent's existing NNP state for the browser/main-process relaunch only, with a regression test starting from NNP=0. I have not supplied a speculative patch against unrelated public CLI code.
  • Searched existing reports for NoNewPrivs, "no new privileges", and Linux relaunch/sudo. #7577 concerns ordinary CLI sandbox warnings; #8222 concerns a systemd-launched CLI. Neither documents this dynamically traced desktop relaunch path.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the Linux desktop/Owl runtime relaunch path behind app.relaunch(), since the relevant implementation is not in public openai/codex. Reproduce from a normal NoNewPrivs: 0 shell with the isolated app and strace command, comparing fresh launch with relaunch. Done means the browser/main-process relaunch preserves the parent's existing NNP state while restricted sessions retain their restrictions, with a regression test.

Written by the indexing model from the issue text.

Assessment

Tech stack
electron, linux, rust
Domain
desktop-dev, operating-systems, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.