Problem
Landlock REFER (cross-directory rename/link) always fails with EXDEV (Invalid cross-device link), even when --rw is specified for the directory.
Reproduction
mkdir -p /tmp/refer-test/subdir
touch /tmp/refer-test/file1
landrun --best-effort --rw /tmp/refer-test --rox /usr,/lib,/bin --ro /proc/self \
-- python3 -c "import os; os.rename('/tmp/refer-test/file1', '/tmp/refer-test/subdir/file1'); print('OK')"
# Result: OSError: [Errno 18] Invalid cross-device link
Root Cause
In internal/sandbox/sandbox.go, landrun calls RestrictPaths() and RestrictNet() separately:
llCfg.RestrictPaths(file_rules...)
llCfg.RestrictNet(net_rules...)
go-landlock's RestrictNet() clears handledAccessFS:
func (c Config) RestrictNet(rules ...Rule) error {
c.handledAccessFS = 0 // Clears FS access rights
return restrict(c, rules...)
}
This creates a second Landlock ruleset with handledAccessFS = 0.
Per kernel docs:
"For historical reasons, the LANDLOCK_ACCESS_FS_REFER right is always denied by default, even when its bit is not set in handled_access_fs."
So even though the first ruleset grants REFER, the second ruleset implicitly denies it. Stacked rulesets are intersected, resulting in denial.
Fix
Use Restrict() instead, which handles both FS and network rules in a single ruleset:
allRules := append(file_rules, net_rules...)
err := llCfg.Restrict(allRules...)
Impact
- jj (Jujutsu VCS) fails - uses rename() for git object store
- Any tool using cross-directory rename/link fails
- npm, cargo, and other package managers may fail with certain operations
Problem
Landlock REFER (cross-directory rename/link) always fails with
EXDEV(Invalid cross-device link), even when--rwis specified for the directory.Reproduction
Root Cause
In
internal/sandbox/sandbox.go, landrun callsRestrictPaths()andRestrictNet()separately:go-landlock's
RestrictNet()clearshandledAccessFS:This creates a second Landlock ruleset with
handledAccessFS = 0.Per kernel docs:
So even though the first ruleset grants REFER, the second ruleset implicitly denies it. Stacked rulesets are intersected, resulting in denial.
Fix
Use
Restrict()instead, which handles both FS and network rules in a single ruleset:Impact