Not able to read/write in directories where the config.toml has configured that directory as a writable root
What version of Codex is running?
codex-cli 0.81.0
What subscription do you have?
Some cisco mega enterprise thingy
Which model were you using?
gpt-5.1-codex-max
What platform is your computer?
Linux 5.15.0-1068-aws (Ubuntu 20.04 kernel)
What terminal emulator and version are you using (if applicable)?
iterm2
What issue are you seeing?
Setting my config.coms with a writable root doesn't make that root writable or readable.
[sandbox]
writable_roots = ["/my/path"]
Running ls /my/path in codex fails with a Landlock failure.
I had codex debug it and write some C scripts to verify it's hypothesis. It believes that the commands happen out of order when setting up the sandbox. Details in additional information.
What steps can reproduce the bug?
Uploaded thread: 019bbebb-c0b6-7111-8994-6ed51163edd9
What is the expected behavior?
When I configure a writable root in my config.toml it is readable and writable.
Additional information
Here is a document I had codex write after debugging the issue:
------
Codex sandbox Landlock failure on AWS kernel
Summary
- Sandboxed commands fail before execution with: "error running landlock: Io(Os { code: 1, kind: PermissionDenied, message: \"Operation not permitted\" })".
- Host kernel supports Landlock and unprivileged Landlock works when
no_new_privsis set first. - This points to a sandbox wrapper ordering issue (calling
landlock_restrict_selfbefore settingno_new_privs).
Environment
- Host: Linux <redacted-hostname> 5.15.0-1068-aws (Ubuntu 20.04 kernel)
- LSMs: lockdown, capability, landlock, yama, apparmor
- Kernel config: CONFIG_SECURITY_LANDLOCK=y, CONFIG_LSM includes landlock
- Codex config:
/home/<redacted-user>/.codex/config.tomlincludes[sandbox] writable_roots = ["/home/<redacted-user>/co/manage"]
Repro
1) Run any sandboxed command, e.g.:
rg --files -g '*.toml' /home/kane/.codex
2) Result: the command fails before execution with Landlock EPERM.
Observed
- Error on sandboxed command:
thread 'main' panicked at linux-sandbox/src/linux_run_main.rs:30:9:error running landlock: Io(Os { code: 1, kind: PermissionDenied, message: "Operation not permitted" })
Expected
- Sandbox should initialize and command should run (and then be restricted by writable_roots).
Landlock probe results
- I ran a minimal C program that calls the Landlock syscalls directly.
- Output:
landlock ABI version: 1landlock_create_ruleset(attr) ok, fd=3landlock_restrict_self (before no_new_privs) failed: errno=1 (Operation not permitted)prctl(PR_SET_NO_NEW_PRIVS) oklandlock_restrict_self (after no_new_privs) ok
Landlock probe source (C)
#define _GNU_SOURCE
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <unistd.h>
#ifndef __NR_landlock_create_ruleset
#define __NR_landlock_create_ruleset 444
#endif
#ifndef __NR_landlock_restrict_self
#define __NR_landlock_restrict_self 446
#endif
#ifndef LANDLOCK_CREATE_RULESET_VERSION
#define LANDLOCK_CREATE_RULESET_VERSION (1U << 0)
#endif
#ifndef LANDLOCK_ACCESS_FS_EXECUTE
#define LANDLOCK_ACCESS_FS_EXECUTE (1ULL << 0)
#endif
struct landlock_ruleset_attr {
uint64_t handled_access_fs;
};
static int ll_create(const struct landlock_ruleset_attr *attr, size_t size, uint32_t flags) {
return (int)syscall(__NR_landlock_create_ruleset, attr, size, flags);
}
static int ll_restrict(int fd, uint32_t flags) {
return (int)syscall(__NR_landlock_restrict_self, fd, flags);
}
int main(void) {
errno = 0;
int abi = ll_create(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
if (abi < 0) {
printf("landlock_create_ruleset(VERSION) failed: errno=%d (%s)\n", errno, strerror(errno));
} else {
printf("landlock ABI version: %d\n", abi);
}
struct landlock_ruleset_attr attr = {0};
attr.handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE;
errno = 0;
int fd = ll_create(&attr, sizeof(attr), 0);
if (fd < 0) {
printf("landlock_create_ruleset(attr) failed: errno=%d (%s)\n", errno, strerror(errno));
return 0;
}
printf("landlock_create_ruleset(attr) ok, fd=%d\n", fd);
errno = 0;
int ret = ll_restrict(fd, 0);
if (ret < 0) {
printf("landlock_restrict_self (before no_new_privs) failed: errno=%d (%s)\n", errno, strerror(errno));
} else {
printf("landlock_restrict_self (before no_new_privs) ok\n");
}
errno = 0;
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
printf("prctl(PR_SET_NO_NEW_PRIVS) failed: errno=%d (%s)\n", errno, strerror(errno));
} else {
printf("prctl(PR_SET_NO_NEW_PRIVS) ok\n");
}
errno = 0;
int fd2 = ll_create(&attr, sizeof(attr), 0);
if (fd2 < 0) {
printf("landlock_create_ruleset(attr) after no_new_privs failed: errno=%d (%s)\n", errno, strerror(errno));
return 0;
}
errno = 0;
ret = ll_restrict(fd2, 0);
if (ret < 0) {
printf("landlock_restrict_self (after no_new_privs) failed: errno=%d (%s)\n", errno, strerror(errno));
} else {
printf("landlock_restrict_self (after no_new_privs) ok\n");
}
return 0;
}
Analysis
- On this host, Landlock is available and works, but requires
no_new_privs=1for unprivilegedlandlock_restrict_self. - The sandbox wrapper appears to call Landlock before setting
no_new_privs, causing EPERM and aborting before any command executes.
Impact
- All sandboxed commands fail immediately, even when operating in configured writable roots.
- Escalated (non-sandboxed) runs work and can access
/home/<redacted-user>/co/manage.
Suggested fix
- Ensure the sandbox wrapper sets
PR_SET_NO_NEW_PRIVSbefore invokinglandlock_restrict_self. - Optionally add a clearer error message when Landlock fails due to missing
no_new_privs.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗