Re: [PATCH RFC 0/3] landlock: Add access rights on created files

From
Günther Noack <hidden>
Date
2026-10-25 11:07:18
Also in
linux-doc, linux-kselftest, lkml
Reply to
Farid Zakaria, 2026-10-24
Hello Farid!

On Sat, Oct 24, 2026 at 01:39:16PM -0700, Farid Zakaria wrote:
This is a proposal for an idea to add support for landlock to work
better with build systems such as Nix where they want to apply
restrictions but the paths may not yet exist.

TL;DR;
========

Landlock rules attach to inodes that exist when a ruleset is built.
That makes a directory shared between trusted and sandboxed tasks hard
to confine: the sandbox may create entries there with
LANDLOCK_ACCESS_FS_MAKE_*, but no rule can name those entries ahead of
time, and granting read access on the directory to cover them exposes
every other entry as well. This series adds a rule flag that grants
access rights on the entries a domain creates in a directory, instead
of on the directory.

Motivation
==========

The motivating user is the Nix build sandbox. Every Nix build runs in
its own sandbox, and a build may only read its declared inputs in
/nix/store and must be able to write and read back the outputs it
creates there. Nix enforces this with a private mount namespace: it
creates an empty store directory and bind-mounts each input path into
it, one mount per input. Nix has paid this per-input cost since the
sandbox was introduced [1].

The cost is paid per build and grows with the size of the input
closure. Nix is moving towards much finer-grained builds (many small
derivations instead of a few large ones), so the number of sandboxes
grows while each build's own work shrinks, and the sandbox setup is
becoming the bottleneck. A typical build has hundreds of inputs; large
ones have thousands.

A microbenchmark sets up and tears down a Nix-like sandbox (user,
mount, PID, IPC, UTS and network namespaces, pivot_root into a chroot,
removing the chroot afterwards). It compares one bind mount per input
with a single bind mount of the whole store plus one Landlock rule per
input. Median latency per sandbox, one sandbox at a time, Linux 7.1,
ext4, 16 cores:

  inputs   bind mount per input   one bind mount + Landlock   speedup
      10         3.2 ms                    2.3 ms               1.4x
     100        11.6 ms                    2.8 ms               4.1x
    1000        79.3 ms                    6.4 ms              12.4x
    5000       400.1 ms                   24.4 ms              16.4x

With Landlock, an input costs an O_PATH open and a landlock_add_rule()
call: there is a single mount, and no mountpoint directory to create
before the build and remove after it (removing the 1000 empty
mountpoints alone took 17 ms).

The single bind mount is only sound if the store listing and the
undeclared store paths stay unreadable while the build can still use
its outputs. Without this series, covering the outputs requires a rule
on /nix/store itself, which makes every store path readable again.

[1] https://github.com/NixOS/nix/issues/179
I have not done a thorough code review yet, and will focus on the
general approach.

I am generally in favor of this approach where we keep track of the
directory inode plus the file name.

Other projects that have run into the problem before are:

(1) Justine Tunney's Landlocked Make experiment
    (https://github.com/jart/landlock-make).  This one shares the same
    "build system" problem, I believe the topic came up during code
    review at the time.

    So I think this is a general problem in build systems that use
    Landlock.

(2) Michael Stapelberg's Go rsync implementation.

    https://github.com/gokrazy/rsync/commit/f85bc9b98cdda705ff8e76ebcd8fba03c6560467

In the rsync case, the issue is not a rule for *creation*, but for
*reading* files.  But it could still be resolved with the same
mechanism: When allow-listing the /etc/resolv.conf file, the policy
works for a while, but eventually a system-level network management
daemon might exchange the file for a new one with an atomic rename,
changing the inode number.  It would be desirable to express a policy
in this case as well, which is tied to the /etc inode plus the
"resolv.conf" filename, for opening files for reading.

The same problem also exists for allow-listing of Pathname Unix Domain
Sockets, where the common way to restart these is to unlink the socket
file and recreate it during bind(2).

In both cases, the best current workaround is to grant the access
right on the parent directory, which is arguably often granting more
than necessary.

Do you think your patch would generalize for the non-creation use
cases as well?

Series
======

Patch 1 lets an LSM observe inode creation through the
inode_init_security hook when no LSM provides an xattr. Patch 2 adds
the LANDLOCK_ADD_RULE_CREATED flag for path-beneath rules and bumps the
ABI to 13. Patch 3 adds selftests.
I only had a short look so far, but stumbled across the words "package
store" / "store" etc.  In other packaging systems, I have not come
across the term "package store" before.  In the kernel code, please
let's not assume that readers have a familiarity with Nix, and let's
please explain features in more general terms.  (Note to self, on
review, grep for the word "store".)

(FWIW, I suspect that this wording might have been put there by an AI.
I've observed in the past that AIs sometimes have an unfortunate
tendency to blur the lines between abstraction layers and
cross-reference implementation internals across abstraction layers.
Maybe something to watch out for?)

Testing
=======

The series applies to the Landlock next branch (3e271a27c46f) and was
tested on Linux 7.3 with that branch applied.

Without patch 2, the five new tests and the ABI version check fail.

Open questions
==============

- A created inode stays pinned until the layer's hierarchy is freed, so a
  sandbox that creates many entries in such a directory keeps them all in
  memory. Should this be bounded?
Is that a preexisting problem that predates your patch?

quoted hunk ↗ jump to hunk
- Creation is detected in inode_init_security, so on filesystems that do
  not call security_inode_init_security() created entries get no rights.
  Is there a better hook?
We are already hooking into hook_path_mknod(), hook_path_symlink()
etc.  Is the feature not possible based on that hook as well?

In fact, I would have assumed that you would only need to change the
logic in the path walk so that it can pick up the special rule type?
Does that not work?
quoted hunk ↗ jump to hunk
- Through overlayfs, created entries get no rights either, because the
  upper inode is created with the mounter's credentials. Should overlayfs
  be handled, e.g. via security_dentry_create_files_as()?
Landlock does generally provide the normal semantics when you use
files through an OverlayFS mount.  We have exhaustive tests for it in
fs_test.c as well.

- A file renamed or linked in keeps only the rights it had at its source.
  Should it get none instead?
Please see the logic and discussions around the
LANDLOCK_ACCESS_FS_REFER access right: When linking or renaming a file
to a new location, we must make sure that a landlocked program can not
move it to a place where it suddenly has more access rights on the
file because it is now accessible under different parent directories.

Thanks,
–Günther
Thread (17 messages) 17 messages, 5 authors, last reply 2d ago
Keyboard shortcuts
hback out one level
jnext message in thread / next series-diff position
kprevious message in thread / previous series-diff position
ldrill in
Escclose help / fold thread tree
?toggle this help