Daan De Meyer e50ec930c2 nsresourced: refuse writable overlayfs whose upper outlives the namespace
A write through an overlay mount does not create the object on the overlay
itself: overlayfs creates it on the upper layer via vfs_create(), which
reaches only the *inode* LSM hooks, not the *path* hooks the transient
range policy attaches to. The one path hook that does fire sees the overlay
mount, whose superblock belongs to the (managed) mounting namespace and so
looks like it dies with it -- while the upper may be a persistent file
system on which the transient IDs then persist.

  Regular file system -- caught by the path hook:

    openat(dir_on_persistent_fs, "f", O_CREAT)
      path_openat -> ... -> security_path_mknod(path{mnt = persistent mount})   << path hook (attached)
        userns_restrict_path_mknod -> validate_mount()
          sb->s_user_ns = init   -> not below managed   (outlives the ns)
          mnt_idmap does not remap the transient id      (reaches disk)
        => -EPERM                                        BLOCKED

  Overlay write -- slips past the path hook (this is the hole):

    openat(overlay_mnt/"f", O_CREAT)
      path_openat -> ... -> security_path_mknod(path{mnt = OVERLAY mount})       << path hook (attached)
        validate_mount(): overlay sb->s_user_ns == managed
        => "dies with the namespace"                     ALLOW
      ovl_create_or_link -> vfs_create(UPPER dentry)
        security_inode_create(UPPER inode)               << inode hook (NOT attached)
        => no check
      inode created on the UPPER fs, owned by the transient uid   PERSISTS

Judge the upper at mount time instead, in lsm/sb_kern_mount:

    mount("overlay", ..., upperdir=<X>, workdir=<Y>)
      do_new_mount -> vfs_get_tree
        ovl_fill_super
          stamps work/index onto the upper via vfs_mkdir, owned by the
          transient mounter -> security_inode_mkdir only (not a path hook)
        security_sb_kern_mount(sb)                        << hook (attached)
          userns_restrict_sb_kern_mount:
            sb->s_magic != OVERLAYFS_SUPER_MAGIC       -> return 0   (not an overlay)
            find_managed_userns(sb->s_user_ns) == none -> return 0   (unmanaged mounter)
            upper = ((struct ovl_fs*)sb->s_fs_info)->layers[0].mnt
            upper == NULL                              -> return 0   (read-only, no upper)
            fs_userns = upper->mnt_sb->s_user_ns
              upper dies with the managed ns (e.g. a tmpfs)         => return 0  ALLOW
              upper persistent, idmap records no transient id       => return 0  ALLOW
              upper persistent, an idmap extent records a transient => -EPERM   REFUSE

The upper has to be judged at mount time because overlayfs performs the
actual upper creations (both the mount-time work/index dirs and every later
copy-up) with internal vfs_create()/vfs_mkdir() calls that reach only the
inode hooks, on the upper's own superblock, where neither the overlay's
ovl_fs nor the mount idmapping is available. Refusing the mount stops every
such write. It cannot undo the work/index dirs ovl_fill_super() already
stamped onto the upper before this hook runs. Reaching those takes an
upperdir and a workdir that already exist on the same persistent,
non-idmapped file system and that the transient IDs may write to: the client
cannot create them itself, since that is what the path hooks deny.

The idmap check walks the upper mount's uid_map and gid_map and permits the
overlay only when no extent records a transient id on disk (ids the idmap
does not map fail the write, so they are fine too). This is what lets a
client back a writable overlay with a persistent but id-mapped upper, e.g.
one handed out by mountfsd, while a plain persistent upper stays refused.

The check keys on whether a writable upper exists (layers[0].mnt is
non-NULL), which is fixed for the superblock's lifetime, not on the mutable
SB_RDONLY flag: an overlay mounted read-only but with an upper can be
remounted read-write later without re-entering sb_kern_mount (remount uses a
different LSM hook), so it must be judged now. A genuinely read-only overlay
has no upper and is left alone. Reading the upper layer needs overlayfs'
private struct ovl_fs/ovl_layer, which are absent from vmlinux.h when
overlayfs is built as a module; they are declared as CO-RE type flavors and
guarded by bpf_core_type_exists(), and the service now orders itself after
modprobe@overlay.service so the module's BTF is present by the time the
program is loaded.

A managed (nsresourced-provisioned) user namespace can therefore only mount
a writable overlay whose upper dies with it (e.g. a tmpfs it set up
itself). Callers that need a writable, persistent (non-tmpfs) upperdir --
mkosi, for one -- should use a regular unprivileged user namespace for that
rather than a transient nsresourced range, since such an overlay is refused
for a managed namespace by design.

test-userns-restrict now mounts real overlays: a read-only overlay, a
writable overlay with a tmpfs upper, and a writable overlay whose upper is
an init-owned but idmapped mount are all allowed, while a writable overlay
with a plain init-owned upper is refused with EPERM. The kernel permits that
last mount without the LSM loaded, so the EPERM is the policy's doing.
2026-08-12 10:49:34 +02:00
2022-04-26 09:13:57 +00:00
…
2026-07-08 08:23:04 +02:00
2026-04-18 14:24:47 +01:00
2026-06-17 09:59:34 +00:00
2025-06-05 14:39:20 +02:00
2026-03-06 08:55:55 +01:00
2025-10-07 13:00:12 +01:00
2026-07-28 19:46:34 +09:00

Systemd

System and Service Manager

OBS Packages Status
Semaphore CI 2.0 Build Status
Coverity Scan Status
OSS-Fuzz Status
CIFuzz
CII Best Practices
Fossies codespell report
Translation status
Coverage Status
Packaging status
OpenSSF Scorecard

Details

Most documentation is available on systemd's website.

Assorted, older, general information about systemd can be found in the systemd Wiki.

Information about build requirements is provided in the README file.

Consult our NEWS file for information about what's new in the most recent systemd versions.

Please see the Code Map for information about this repository's layout and content.

Please see the Hacking guide for information on how to hack on systemd and test your modifications.

Please see our Contribution Guidelines for more information about filing GitHub Issues and posting GitHub Pull Requests.

When preparing patches for systemd, please follow our Coding Style Guidelines.

If you are looking for support, please contact our mailing list, join our IRC channel #systemd on libera.chat or Matrix channel

Stable branches with backported patches are available in the stable repo.

We have a security bug bounty program sponsored by the Sovereign Tech Fund hosted on YesWeHack

Repositories with distribution packages built from git main are available on OBS, and also repositories with packages built from the latest stable release

S
Description
systemd patched fork
Readme Cite this repository
409 MiB
Languages
C 88.5%
Shell 5.7%
Python 4.6%
Meson 1.1%