anthropics / anthropics/sandbox-runtime

Linux bwrap: allowWrite paths become read-only when allowRead parent overlaps under a denyRead region

Open
#263 0 comments 3 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
5.2k
Forks
439
Avg merge
2d 9m
Merged PRs (30d)
13

Description

## Summary

When the filesystem config specifies `denyRead` and `allowRead` on overlapping regions, `allowWrite` paths nested under an `allowRead` ancestor silently become read-only. The sandbox command fails with `EROFS` or `Read-only file system` on write operations that should be permitted.

## Reproduction

### Config

```typescript
import { wrapCommandWithSandboxLinux } from './src/sandbox/linux-sandbox-utils.js'

const wrapped = await wrapCommandWithSandboxLinux({
command: 'touch /tmp/srt-test/workspace/project/hello.txt',
needsNetworkRestriction: false,
readConfig: {
denyOnly: ['/tmp/srt-test'],
allowWithinDeny: ['/tmp/srt-test/workspace'],
},
writeConfig: {
allowOnly: ['/tmp/srt-test/workspace/project'],
denyWithinAllow: [],
},
})
```

### Setup

```bash
mkdir -p /tmp/srt-test/workspace/project
```

### Command

```bash
eval "$wrapped"
# → touch: cannot touch '/tmp/srt-test/workspace/project/hello.txt': Read-only file system
```

## Expected vs Actual Behavior

**Expected:** `/tmp/srt-test/workspace/project/hello.txt` is created successfully. `allowWrite` paths should remain writable regardless of `allowRead` configuration.

**Actual:** The write fails with `Read-only file system`. The `allowWrite` path is silently downgraded to read-only.

## Root Cause

In `generateFilesystemArgs()` (`src/sandbox/linux-sandbox-utils.ts`), when processing a `denyRead` directory, the function:

1. Mounts `--tmpfs` over the denied path (wiping everything under it)
2. Re-binds `allowWrite` paths with `--bind` (writable)
3. Re-allows `allowRead` paths with `--ro-bind` (read-only)

Since bwrap processes mounts in argument order, a later mount on a parent path **hides** earlier mounts on child paths. When `allowRead` is a parent of `allowWrite`, step 3's `--ro-bind` overwrites step 2's `--bind`:

```
--tmpfs /tmp/srt-test
--bind /tmp/srt-test/workspace/project /tmp/srt-test/workspace/project
--ro-bind /tmp/srt-test/workspace /tmp/srt-test/workspace ← HIDES the --bind above
```

## Fix

Swap the loop order: emit `allowRead` ro-binds **before** `allowWrite` re-binds. Write mounts then layer on top and remain writable.

## Environment

- **OS:** Linux (any distro with bubblewrap)
- **bwrap version:** any (mount ordering is fundamental to bwrap's design)
- **sandbox-runtime:** current `main`

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.