Commit Graph
109 Commits
Author SHA1 Message Date
Clayton Craft bc6380ae07 nsresourced: fix BPF loading when using kernel compiled with Clang
This fixes an issue where nsresourced fails to load BPF on kernels
compiled with Clang (this output was from v259):

    $ sudo env SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-nsresourced
    ; int BPF_PROG(userns_restrict_path_chown, struct path *path, void* uid, void *gid, int ret) { @ userns-restrict.bpf.c:134
    ...
    ; return validate_path(path, ret); @ userns-restrict.bpf.c:135
    ...
    ; static int validate_path(const struct path *path, int ret) { @ userns-restrict.bpf.c:120
    ...
    ; task = (struct task_struct*) bpf_get_current_task_btf(); @ userns-restrict.bpf.c:84
    ...
    ; task_userns = task->cred->user_ns; @ userns-restrict.bpf.c:85
    ...
    R2 invalid mem access 'rcu_ptr_or_null_'

When Clang is used (which sets CONFIG_PAHOLE_HAS_BTF_TAG), btf_type_tag
support is enabled. As a result, an rcu type tag is added to
task_struct::cred:

    $ bpftool btf dump file /sys/kernel/btf/vmlinux | grep "STRUCT 'task_struct'"
    [459] STRUCT 'task_struct' size=4672 vlen=242

    $ bpftool btf dump file /sys/kernel/btf/vmlinux | grep -A200 "^\[459\] STRUCT 'task_struct'" | grep cred
	    'ptracer_cred' type_id=802 bits_offset=14528
	    'real_cred' type_id=802 bits_offset=14592
	    'cred' type_id=802 bits_offset=14656

    $ bpftool btf dump file /sys/kernel/btf/vmlinux | grep '^\[802\]'
    [802] PTR '(anon)' type_id=801

    $ bpftool btf dump file /sys/kernel/btf/vmlinux | grep '^\[801\]'
    [801] TYPE_TAG 'rcu' type_id=803

Since the struct ptr *could* be null, we have to add a null pointer
check to satisfy the bpf verifier.
2026-03-20 15:27:08 +01:00
Luca Boccassi 5647a19a58 nsresourced: downgrade benign log message to debug
This is very noisy as there's a dozen of these message every
time it gets called, and it's not really an error but an
expected situation, so downgrade from info to debug
2026-03-10 18:23:14 +01:00
Daan De Meyer 838528104b nsresourced: Optionally map foreign UID range
Whenever delegating UID ranges to a user namespace, it can also be
useful to map the foreign UID range, so that the container running in
the user namespace with delegated UID ranges can download container
images and unpack them to the foreign UID range.

Let's add an option mapForeign to make this possible. Note that this option
gives unprivileged users full access to the any foreign UID range owned directory
that they can access. Hence it is recommended (and already was recommended) to
store foreign UID range owned directories in a 0700 directory owned by the
owner of the tree to avoid access and modifications by other users.

This is already the case for the main users of the foreign UID range,
namely /var/lib/machines, /var/lib/portables and /home/<user> which all
use 0700 as their mode.

Users will also be able to create foreign UID range owned inodes in any
directories their own user can write to (on most systems this means /tmp,
/var/tmp and /home/<user>).
2026-02-24 18:29:37 +01:00
Daan De Meyer 95d05079bf nsresourced: Add support for self mappings with delegations
mkosi does all of its environment setup in an unprivileged user
namespace with an identity mapping. When it invokes nspawn and nspawn
tries to get a transient userns from nsresourced, this fails as no
transient ranges are mapped into mkosi's unprivileged userns (as doing
so would require privileges).

To fix this problem, let's allow allocating unprivileged self user
namespaces in nsresourced, similar to what the kernel allows, except that
we also support delegations for these. This means that mkosi can get its
unprivileged userns as before from nsresourced, but it can also request a
delegated 64K range inside that userns as well, which nsresourced can then
allocate to nspawn later when it asks for one.

Similar to the kernel, we disallow setgroups for self mappings. However,
instead of doing this via /proc/self/setgroups, which applies to the current
user namespace and all its child user namespaces, we use the BPF LSM to deny
setgroups instead, so that it can still be allowed for child user namespaces.
We need this because as soon as a container launches in a child user namespace
using one of the delegated mappings, it has to be able to do setgroups() to be
able to function properly.

To allow mapping the root user, we need to add the CAP_SETFCAP capability to
nsresourced.
2026-02-24 18:29:37 +01:00
Daan De Meyer 025439faaa nsresourced: Add support for delegated ranges
We want to support the scenario where we bind mount the nsresourced
varlink socket into a container to allow nested containers where the
outer container runs in its own transient range from nsresourced but
can still allocate transient ranges for its own nested containers.

To support this use case let's add support for delegation. Delegated
ranges are allocated when allocating the primary range and are propagated
1:1 to the user namespace. We track delegated ranges in ".delegate" files
in the userns registry so that they can't be used for other range allocations.

We make one exception for delegated ranges though, if we get a request from
a user namespace that is a child of the user namespace that owns the delegated
ranges, we allow allocating from the delegated range. The parent userns already
has full ownership over the child userns, so it doesn't matter that the parent
userns and the child userns share the same range. This allows making use of
delegated ranges without having to run another copy of nsresourced inside the
parent userns to hand out from the delegated range.

To support recursive delegations, we keep track of the previous owners of the
delegated range and restore ownership to the last previous owner when the current
owner is freed.
2026-02-24 18:29:37 +01:00
Daan De Meyer 6c2d437585 nsresourced: Allow operations outside transient UID/GID ranges
The purpose of the userns-restrict BPF-LSM program is to prevent the
transient ranges leaking to disk, so let's allow operations outside the
transient UID ranges, even if the mount is not allowlisted.

This is preparation for the next commits where we'll add support for mapping
the current user and the foreign UID range into the user namespaces provisioned
by nsresourced. Operations creating files/directories as these UIDs/GIDs should
not need the corresponding mount to be allowlisted with nsresourced.
2026-02-24 18:29:37 +01:00
Daan De Meyer d28c96d85e userns-restrict: Remove unused inode argument and rename function 2026-02-19 21:33:11 +01:00
Daan De Meyer fd99ead01d test-userns-restrict: Migrate to new assertion macros
We also inline the test functions so we get proper line information
in the failure coredumps.
2026-02-19 21:33:11 +01:00
Daan De Meyer dd8c07da04 Various fixes related to mountfsd (#40564) 2026-02-05 16:39:28 +01:00
Christian Brauner e8416e854b nsresourced: Ensure that all user namespaces are cleaned-up
The code here assumes that free_user_ns() is called for every single
user namespace. That however has never been the case and the logic for
free_user_ns() is a bit more involved.

A nested user namespace pins its parent user namespace. IOW, the
lifetime of the parent user namespaces is at least as long as the child
user namespaces.

If a parent user namespace becomes unused (no namespace file descriptors
or task using it anymore) then it will stick around and its lifetime
still bound to the child user namespace.

free_user_ns() takes advantage of that behavior. If a child user
namespace is freed and its parent user namespace is already unused then
then free_user_ns() will free both the child and the parent user
namespace. This means a single free_user_ns() frees two user namespaces.
Hence, the bpf program never sees the parent user namespace being freed.

We can fix this by piggy-backing on another function that is called for
every single user namespace being freed. This requires CONFIG_SYSCTL but
systemd doesn't work without that anyway.

The return type needs to change to a scalar type as required by libbpf.

Long-term what we need is appropriate LSM infrastructure for this
including hooks that get called on namespace destruction.

Thanks to Daan DeMeyer for figuring out that the cast is needed.

Signed-off-by: Christian Brauner <brauner@kernel.org>
2026-02-05 15:32:15 +01:00
Daan De Meyer 14054134c6 nsresourced: Add comment about mknod() hook covering regular files 2026-02-05 15:16:31 +01:00
Daan De Meyer d1ae0e5205 nsresourced: Fix typo 2026-02-05 15:16:31 +01:00
Yu Watanabe f37931bd78 tree-wide: replace cg_{fd,path}_get_cgroupid() with {fd,path}_to_handle_u64() 2026-02-04 02:45:17 +09:00
Daan De Meyer 52ecf376fa tree-wide: Migrate to pidref_safe_fork()
Let's migrate all remaining callers of safe_fork()
to pidref_safe_fork() and get rid of safe_fork().
2026-01-02 20:08:41 +01:00
Daan De Meyer 29d1df4b03 tree-wide: Remove unnecessary SIGCHLD blocking
Now that sd-event doesn't insist on blocking
SIGCHLD anymore when watching for process exit,
let's stop blocking SIGCHLD tree-wide where not
required anymore.
2025-12-20 15:50:47 +01:00
Daan De Meyer fbd276cb86 tree-wide: Use pidref_wait_for_terminate_and_check()
Let's use the pidref variants for waiting and
checking a process everywhere and get rid of the
non pidref variants.
2025-12-20 15:50:47 +01:00
Yu Watanabe 93378148d4 tree-wide: normalize comment style 2025-12-15 08:50:13 +09:00
Yu Watanabe 929ce35f8d meson: rework include_directories() and finding of tools (#39956)
This is a cleanup in preparation for later additions.
2025-12-02 20:07:37 +09:00
Zbigniew Jędrzejewski-Szmek 963bebd7b0 meson: add source dir to include_directories automatically
Quite often we need to specify include_directories('.'). Normally, meson
does this automatically, but we specify implicit_include_directories : false,
see 30d20907bd for an explanation.
Passing the include_directories argument through the template layers was rather
cumbersome. Let's simplify things by always including the directory of the
first source file. This makes the definitions simpler, at the cost of having an
unnecessary include directory in the list in some cases.

(Tests are excluded from this change, because they happen to not need this, but
also because some tests have source[0] which is a custom target, and we'd need
newer meson to support that.)
2025-12-01 15:35:22 +01:00
Daan De Meyer 6a9f0641cd tree-wide: Fix declaration/definition parameter name mismatches 2025-11-26 19:02:31 +09:00
Yu Watanabe caf7cb2737 SD_JSON_BUILD_PAIR() conversions with coccinelle script (#39858) 2025-11-26 16:35:35 +09:00
Daan De Meyer 9a6a8f35eb tree-wide: Small fixes for various clang-tidy warnings 2025-11-26 16:22:00 +09:00
Jelle van der Waa c12966c14f treewide: prefer using SD_JSON_BUILD_PAIR_* over SD_JSON_BUILD_PAIR()
Include a coccinelle script that patches this automatically.
2025-11-24 21:05:10 +01:00
Daan De Meyer 87fbd33372 clang-tidy: Fix all remaining misc-include-cleaner violations
- Remove unused includes
- Add common false positive headers to misc-include-cleaner.IgnoreHeaders
- Add IWYU pragma keep for uncommon false positive headers
2025-11-22 10:19:41 +01:00
Zbigniew Jędrzejewski-Szmek 5220bb8c3f meson: automatically add 'extract' to 'sources'
In all cases, everything that we list in 'extract', we also list in
'sources'. We can simplify things by automatically appending the first
list to the second.

In the listings, move 'extract' key right below 'sources', since now
they are both "sources", just with slightly different meanings.
2025-11-20 10:03:48 +09:00
Yu Watanabe 5963cdf498 tree-wide: add missing '#pragma once' 2025-11-05 05:16:34 +09:00
Lennart Poettering aea2f059a3 tree-wide: use setenvf() more, where appropriate 2025-10-22 13:36:18 +02:00
Daniel Foster c7a444a9c1 tree-wide: extend $LISTEN_FDS protocol with $LISTEN_PIDFDID
Although extremely unlikely, there is a race present in solely checking the
$LISTEN_PID environment variable, due to PID recycling. Fix that by introducing
$LISTEN_PIDFDID, which contains the 64-bit ID of a pidfd for the child process
that is not subject to recycling.
2025-10-22 09:34:14 +02:00
Daan De Meyer f102bc3e5f tree-wide: Introduce sd-forward.h and shared-forward.h headers
Let's not leak details from src/shared and src/libsystemd into
src/basic, even though you can't actually do anything useful with
just forward declarations from src/shared.

The sd-forward.h header is put in src/libsystemd/sd-common as we
don't have a directory for shared internal headers for libsystemd
yet.

Let's also rename forward.h to basic-forward.h to keep things
self-explanatory.
2025-10-16 17:00:29 +02:00
Lennart Poettering 3ef74e44b8 nsresourced: use a hashed rather than a mangled name as fallback
If we are asked to come up with our own name for the namespace to
allocate (because client enabled "mangle"), then we so far created a
randomized name if shortening what was proposed didn't work. This broke
polkit authorization however, because when polkit is in the mix, we
process method calls twice, submitting the polkit request on the first
and then assuming a response is known on the second invocation. But if
we generate a randomized name for the two checks we'll not be ablet to
match up the requests because it's going to be different. Let's fix that
by not using a randomized name, but one hashed from the socket
connection we are processing mixed with the client provided name. This
will ensure that for the same method call we'll generate the same name,
but different calls (i.e. calls with different names on the same socket,
or with any name on any socket) we'll end up with different names,
minimizing chance of collision.

This ensures PK starts to work with nsresourced userns registration when
a bad or no name is specified, which previously would end up in a PK
query loop.
2025-08-26 15:30:39 +02:00
Yu Watanabe 5c34d85c74 tree-wide: normalize usage of %m 2025-07-22 03:05:54 +09:00
Yu Watanabe 5918ba9c69 tree-wide: fix log messages: "Failed VERB" -> "Failed to VERB" 2025-07-22 03:05:33 +09:00
DaanDeMeyer f4df5902d8 bpf: Use hyphens in directory names instead of underscores 2025-07-13 12:45:37 +02:00
Yu Watanabe 3fc2a44043 include: move trivial kernel header wrappers to src/include/override/ 2025-07-11 13:05:46 +09:00
Yu Watanabe 543a48b653 libc-wrapper: introduce a tiny libc wrapper
Then, move syscall definitions to the wrapper, and prototypes are moved
to relevant headers.

This also adds checks for add_key() and request_key(), as one day
glibc may be going to add some of them separatedly.

The check for fspick in meson.build is dropped, as it is currently
unused in our code.

This also moves
- basic/missing_bpf.h -> include/override/linux/bpf.h,
- basic/missing_keyctl.h -> include/override/linux/keyctl.h.
2025-07-11 13:05:46 +09:00
Yu Watanabe 8d29f31bf6 namespace-util,nsresource: explicitly include sched.h
These source files uses symbols provided by sched.h, e.g.
setns(), unshare(), CLONE_NEWNS, and friends, but they do not explicitly
include sched.h. Currently, it is included indirectly via missing_syscall.h,
which is included by e.g. pidfd-util.h.
Let's explicitly include headers that provides symbols used in the code.

This is similar to 4f18ff2e29, but for sched.h.
2025-07-06 13:28:46 +02:00
Yu Watanabe 237bdd2771 basic: move missing_sched.h -> include/sched.h 2025-07-01 12:29:15 +09:00
Yu Watanabe 4f18ff2e29 tree-wide: include unistd.h where necessary
We use symbols provided by unistd.h without including it. E.g.
open(), close(), read(), write(), access(), symlink(), unlink(), rmdir(),
fsync(), syncfs(), lseek(), ftruncate(), fchown(), dup2(), pipe2(),
getuid(), getgid(), gettid(), getppid(), pipe2(), execv(), _exit(),
environ, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, F_OK, and their
friends and variants, so on.

Currently, unistd.h is indirectly included mainly in the following two paths:
- through missing_syscall.h, which is planned to covert to .c file.
- through signal.h -> bits/sigstksz.h, which is new since glibc-2.34.
  Note, signal.h is included by sd-eevent.h. So, many source files
  indirectly include unistd.h if newer glibc is used.

Currently, our baseline on glibc is 2.31. We need to support glibc older
than 2.34, but unfortunately, we do not have any CI environments with
such old glibc. CIFuzz uses glibc-2.31, but it builds only fuzzers, and
many files are even not compiled.
2025-06-30 09:19:15 +02:00
Zbigniew Jędrzejewski-Szmek 7d247d3cb8 meson: drop explicit custom_target names
[1] says:
> Since 0.60.0 the name argument is optional and defaults to the basename of
> the first output
We specify >= 0.62 as the supported version, so drop the duplicate name in all cases
where it is the same as outputs[0], i.e. almost all cases.

[1] https://mesonbuild.com/Reference-manual_functions.html#custom_target
2025-06-28 17:14:50 +02:00
Jelle van der Waa a2f7e2c49d nsresourcework: correct argument comment for userns_registry_* 2025-06-04 15:46:11 +02:00
Jelle van der Waa 38868c0b0d nsresourcework: correct argument comments for inode_same_at 2025-06-04 15:39:17 +02:00
Jelle van der Waa ac3a403486 treewide: correct argument comments for sd_event_add_time 2025-06-04 13:32:24 +02:00
Jelle van der Waa 095031683f treewide: correct argument comments for sd_netlink_call 2025-06-04 13:32:24 +02:00
Jelle van der Waa 1cce9d930a treewide: correct argument comment to 'ret' where applicable
For functions with one return argument prefer 'ret' as argument comment.
2025-06-04 13:32:24 +02:00
Jelle van der Waa 7a4eb66ee5 nsresourced: correct argument comments 2025-06-04 10:52:59 +02:00
Jelle van der Waa 93117b0fbd cgroup-util: rename 'fsfd' -> 'cgroups_fd'
Makes the header and implementation consistent.
2025-06-04 10:45:54 +02:00
Zbigniew Jędrzejewski-Szmek 42ba99748d various: do not include file names directly in error messages
git grep -l 'Failed to open /'|xargs sed -r -i 's|"Failed to open (/[^ ]+): %m"|"Failed to open %s: %m", "\1"|g'
git grep -l $'Failed to open \'/'|xargs sed -r -i $'s|"Failed to open \'(/[^ ]+)\': %m"|"Failed to open %s: %m", "\\1"|g'
git grep -l "Failed to open /"|xargs sed -r -i $'s|"Failed to open (/[^ ]+), ignoring: %m"|"Failed to open %s, ignoring: %m", "\\1"|g'
+ some manual fixups.
2025-06-02 11:10:38 +02:00
Daan De Meyer 86b9a3e31d tree-wide: Clean up includes
This removes more unused includes from the components already cleaned
up in #37467 and #37457.

Split out of #37344.
2025-05-21 10:49:42 +02:00
Daan De Meyer a583b34416 meson: Extract more objects instead of compiling multiple times
Also, let's deflatten the lists of sources in preparation for the
next commit at the same time.

In systemctl, we split out systemctl-main.c to make sure the definition
of main() is in a separate object which allows us to extract the systemctl.c
object and link it in the fuzzer target without getting a multiple definition
error when linking.
2025-05-20 10:29:58 +02:00
Daan De Meyer a1e519910b nsresourced: Clean up includes
Split out of #37344.
2025-05-15 00:01:59 +02:00