Luca Boccassi [Sat, 28 Mar 2026 20:10:14 +0000 (20:10 +0000)]
importd: add assert for log_message_size accumulation bounds
Coverity flags log_message_size += l as a potential overflow, but l
is bounded by the read() count parameter which is
sizeof(log_message) - log_message_size. Add an assert to make this
invariant explicit.
Luca Boccassi [Sat, 28 Mar 2026 20:08:55 +0000 (20:08 +0000)]
sd-bus: add asserts for rbuffer_size accumulation bounds
Coverity flags rbuffer_size += k as a potential overflow, but k is
always bounded by the iov size (which is the difference between the
allocated buffer and current rbuffer_size). Add asserts to make this
invariant explicit.
Luca Boccassi [Sat, 28 Mar 2026 19:55:35 +0000 (19:55 +0000)]
uid-range: add asserts to document overflow safety in coalesce
Coverity flags the x->start + x->nr and y->start + y->nr additions
as potential overflows. These are safe because uid_range_add_internal()
validates start + nr <= UINT32_MAX before inserting entries. Add asserts
to document this invariant for static analyzers.
Luca Boccassi [Sat, 28 Mar 2026 19:52:09 +0000 (19:52 +0000)]
sd-event: add assert to help static analysis trace signal bounds
Coverity flags the signal_sources array access as a potential
out-of-bounds read because it cannot trace through the SIGNAL_VALID()
macro to know that ssi_signo < _NSIG. Add an explicit assert after
the runtime check to make the constraint visible to static analyzers.
Luca Boccassi [Sat, 28 Mar 2026 19:49:20 +0000 (19:49 +0000)]
cpu-set-util: add asserts to guide static analysis after realloc
Coverity flags CPU_SET_S() calls as potential out-of-bounds writes
because it cannot trace that cpu_set_realloc() guarantees the
allocated buffer is large enough for the given index. Add asserts
to make the size invariant explicit.
Luca Boccassi [Sat, 28 Mar 2026 19:47:27 +0000 (19:47 +0000)]
debug-generator: use unsigned bit shift for breakpoint flags
Using signed int literal '1' in left shift can lead to undefined
behavior if the shift amount causes overflow of a signed int. Use
UINT32_C(1) since the result is stored in a uint32_t variable.
Luca Boccassi [Sat, 28 Mar 2026 19:35:36 +0000 (19:35 +0000)]
scsi_id: use strscpy instead of strncpy for wwn fields
strncpy does not null-terminate the destination buffer if the source
string is longer than the count parameter. Since wwn and
wwn_vendor_extension are char[17] and we copy up to 16 bytes, there's
a risk of missing null termination. Use strscpy which always
null-terminates.
Luca Boccassi [Sat, 28 Mar 2026 18:55:37 +0000 (18:55 +0000)]
stat-util: add assert to silence coverity
Coverity thinks _mntidb can be used uninitialized, but this
is not the case when r == 0. Add a bool variable to make it
clearer instead of reusing 'r' later, and an assert to guide
static analyzers.
Luca Boccassi [Fri, 27 Mar 2026 20:59:46 +0000 (20:59 +0000)]
mkosi: pull in gnu coreutils for Ubuntu 26.04 and newer
The default coreutils in Ubuntu 26.04 moved to uutils, which is broken
in many subtle and annoying ways, breaking various tests. It's also
a giant monolithic megabinary which makes the minimal image size
go up and break other tests.
Force the gnu coreutils to be pulled in all images.
Luca Boccassi [Fri, 27 Mar 2026 17:02:41 +0000 (17:02 +0000)]
test: check for bin/bash in dissect --mtree instead of cat
Ubuntu is doing shenanigans with their coreutils so they are now
symlinks instead of binaries, so the grep fails. Check bash instead
to fix test failure on 26.04.
Luca Boccassi [Fri, 27 Mar 2026 19:32:29 +0000 (19:32 +0000)]
shutdown: remove kexec-tools dependency
'kexec -e' is just a small wrapper that does the xen hypercall
on xen, or otherwise just calls reboot(). Drop the dependency,
and reuse the existing xen hypercall helper.
many: more checks for pointer access without NULL check (#41370)
This is a followup for https://github.com/systemd/systemd/pull/41096
that makes more subsystems pass the new `check-pointer-deref` coccinelle
checks. See the individual commits.
My plan is to do a few more of these PRs until we have it all covered. I
could also do it in a single very big PR but I'm worried about a)
conflicts b) that its just too big/annoying to review. Only 7 dirs left
but some (like src/basic) are quite big (~50k loc) so those PRs will be
a bit bigger.
Daan De Meyer [Fri, 27 Mar 2026 11:40:59 +0000 (11:40 +0000)]
vmspawn: improve firmware selection to match mkosi's implementation
Align find_ovmf_config() with mkosi's find_ovmf_firmware() by adding
checks that were previously missing:
- Filter on interface-types, only selecting UEFI firmware definitions.
Previously non-UEFI (e.g. BIOS-only) firmware could be selected.
- Check machine type compatibility using substring matching against the
target machine patterns in firmware descriptions (e.g. "q35" matches
"pc-q35-*"), following the same approach as mkosi.
- Make nvram-template optional in the firmware JSON mapping. Firmware
definitions without an nvram-template are now parsed successfully
(with vars remaining NULL) rather than failing entirely.
Also rework the firmware target parsing to store both architecture and
machine arrays per target (instead of just a flat architecture list),
and extract the machine matching into firmware_data_matches_machine().
Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
Daan De Meyer [Fri, 27 Mar 2026 11:55:32 +0000 (12:55 +0100)]
vmspawn: Add --firmware=describe
It's useful to be able to check what firmware description vmspawn
will select. In particular, this will allow me to figure out the
nvram template file that will be picked up so I can pick it up in
mkosi and operate on it to pass a modified version of it to vmspawn
with --efi-nvram-template=.
Daan De Meyer [Fri, 27 Mar 2026 10:43:26 +0000 (10:43 +0000)]
vmspawn: add --efi-nvram-template= and --firmware-features= options
Add --efi-nvram-template=PATH to specify a custom firmware variables
file to copy and use as the initial EFI NVRAM state instead of the
default template from the firmware definition.
Add --firmware-features=FEATURE[,FEATURE...] to require or exclude
specific firmware features during automatic firmware discovery.
Features prefixed with "!" are excluded. If a feature appears in both
the included and excluded lists, inclusion takes priority. Firmware
with the "enrolled-keys" feature is excluded by default.
Refactor --secure-boot= to operate on the firmware features sets
instead of maintaining a separate tristate. --secure-boot=yes adds
"secure-boot" to the include set, --secure-boot=no adds it to the
exclude set, and --secure-boot=auto removes it from both.
Generalize find_ovmf_config() to accept include/exclude feature sets
instead of a secure boot tristate, removing the special-cased
enrolled-keys and secure-boot filtering logic.
Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
tmpfile-util: don't log about lack of O_TMPFILE support
It's a very common case (vfat...), and it's just too much noise. After
all the whole function exists primarily to deal with O_TMPFILE not being
availeble everywhere...
* 23ef56be00 Install new files for upstream build
* 98645a89ba Install new files for upstream build
* dc2dd78cc0 Install new files for upstream build
* aad316ec34 Drop wildcards, dh_exec does not suppor them for manpages
* 3bf8703dab Install new files for upstream build
* d1e92a6493 Update changelog for 260.1-1 release
* e7a80fe2b8 Install basic.conf in sd-standalone-sysusers package
* 48f796240e Add lpadmin group to basic.conf sysusers.d as requested by CUPS maintainer
* c15703b8aa Update changelog for 260-1 release
* f26cc52a43 Drop version from libselinux-dev dependency
* 7f3701ae2f Do not run "systemctl enable getty@.service" unconditionally
* ec59ddd832 Switch from libselinux1-dev to libselinux-dev
* 35258cd599 Update changelog for 260~rc4-1 release
* eb194c22ff Update changelog for 260~rc3-1 release
* a6878815d6 Really enable getty@ via packaging scriptlets
Michael Vogt [Fri, 27 Mar 2026 10:20:40 +0000 (11:20 +0100)]
coccinelle: document why src/libc/ and src/test/ are excluded
For some of the directories it makes more sense to keep them
excluded from the coccinelle check. Specifically:
- libc: compatibility, no asserts or systemd headers yet
- test: uses NUL internally to test crashes etc
Michael Vogt [Fri, 27 Mar 2026 09:44:03 +0000 (10:44 +0100)]
coccinelle: generalize pidref_is_set() to `=~ _is_set()`
Our coccinelle/check-pointer-deref.cocci checker has a special
case for `assert(pidref_is_set(param))`. It turns out we can
generalize this and catch the following:
- iovec_is_set
- sd_dhcp_duid_is_set
- sd_dhcp_client_id_is_set
bootspec: honour profile number when sorting properly
This corrects sorting of menu entries regarding profile numbers:
1. If the profile number is unset, let's treat this identical to profile
0, when ordering stuff, because an item with no profile is
conceptually the same as an item with only a profile 0.
2. Let's take the profile number into account also if sort keys are
used. This was makes profiles work sensibly in type 1 entries, via
the recently added "profile" stanza.
Also change the order of flags to be more logical. First the option
to specify at what fields we look, then the option to specify how we
return their name, the the value, and finally what to do if the value
is missing.
Michael Vogt [Fri, 13 Mar 2026 13:46:37 +0000 (14:46 +0100)]
coccinelle: add checks for pointer access without NULL check
The fix in 8f1751a111 made me wonder if we could automatically detect
when pointers are accessed but when this might not be safe. Systemd
is already using a lot of `assert(dst)` and this change now forces
us to use them.
So this commit (ab)uses coccinelle to flag any pointer parameter
dereference not preceded by assert(param), ASSERT_PTR(param), or an
explicit NULL check. It adds integration into meson as a new "coccinelle"
test suite (just like clang-tidy) and is run in CI. The check is not
perfect but seems a reasonable heuristic.
For this RFC commit it is scoped to a subset, it excludes 25 dirs right
now and includes around 100. About 300 warnings left. Busywork that I am
happy to do if there is agreement that it is worth it.
With this in place we would have caught the bug from 8f1751a111 in CI:
```
FAIL: check-pointer-deref.cocci found issues in systemd/src/boot:
diff -u -p systemd/src/boot/measure.c /tmp/nothing/measure.c
--- systemd/src/boot/measure.c
+++ /tmp/nothing/measure.c
@@ -312,7 +312,6 @@ EFI_STATUS tpm_log_tagged_event(
if (err != EFI_SUCCESS)
return err;
- *ret_measured = true;
return EFI_SUCCESS;
}
```
This also adds a new POINTER_MAY_BE_NULL() for the cases when the
called function will do the NULL check (like `iovec_is_set()`).
Enabling locking by default would constitute a major footgun and
compatibility break on upgrades. This functionality is useful, but it
requires the rest of the system to be "ported" to use systemd-imds
first. The user or distro should opt in to "locked" mode only after
doing the integration work.
Luca Boccassi [Thu, 26 Mar 2026 15:36:43 +0000 (15:36 +0000)]
labeler: update to latest commit and limit file-based label to 5 (#41358)
When doing large refactors or large changes the bot spams
labels left and right, making the PR unreadable. Use the
new option to limit the bot to a max of 5 file-based
labels. If more than 5 would be set, all file-based labels
are skipped.
optionally run a software TPM at boot as fallback for TPM less machines (#41016)
In various scenarios it's useful to be able to run a software TPM as
fallback on a machine if it doesn't natively have a hardware for it, in
order to provide somewhat systematic interfacing for legacy machines.
This adds the infrastructure for it.
Relevant parts:
1. On EFI systems sd-stub will now generate a random secret on first
boot and store it in a persistent NV variable, which is marked
inaccessible to the OS. It then derives a per-OS secret from that which
is passed to the OS via the initrd logic. This is generally useful, but
in particular is intended to secure the software TPM at least a bit: it
provides better security than nothing (i.e. the only protection in place
is that the firmwrae protections work, but this is also what shim relies
on, hence maybe not too bad), and allows swtpm to encrypt its storage
with something.
2. systemd-tpm2-generator is extended to optionally start an swtpm if no
tpm hw is detected. Because this is of course a major downgrade in
security, this has to be requested explicitly at boot via a kernel
cmdline switch.
3. This optionally mounts the ESP from the initrd. This is general
infrastructure, and has been requested before, but is particularly
interesting in the context of software TPMs: the state needs to be
stored somewhere, and that before the rootfs can be unlocked.
4. This introduces a special new separator measurement for PCRs 0-7 that
isolates all measurements from pre-os/uefi world from those done by the
OS. I added this for three reasons:
- in the swtpm case we'll not have any pre-os/uefi measurements, and we
need to be able to determine cleanly that this is the case. this hence
is supposed to play a similar role as the usual firmware separator
measurement, that however cleanly fixates to the PCRs even the case
where the firmware measurements don't exist at all
- this is a very comprehensive fix for #40567
- not all firmwares generate the firmware separator at all, but it is
essential to seal off firmware variables from OS generated ones. This
can fill the void to some degree.
6. This introduces a new kernel cmdline switch
systemd.tpm2-measured-os=1, which allows force enabling all our
measurement logic, even if UKI TPM measurements are not done. This is
supposed to be used for the swtpm case so that one gets all the
measurements even without having the early boot verified boot chain in
place.
Benefits of all this: systems that care about TPMs have a (lower
security) compat glue in place that allows supporting legacy hw the same
way as modern hw in many ways, so that remote attestation and other uses
can reasonably work with the same codepaths.
Also see: https://github.com/lxc/incus-os/pull/667 regarding prior
similar work.
units: make use of nvpcrs only after the NV anchor completion measurement is done
This makes sure we don't use the "hardware" or "verity" nvpcrs before
the NV anchor measurement is done.
This is mostly to avoid confusing output, and to indirectly ensure the
nvpcr allocation in tpm2-setup is the load bearing one, but it should
not be load bearing for security afaics.
This has been requested previously for PCR 7 (#40567), but let's do that
for all firmware owned PCRs, since some firmwares forget to measure
their own separator. Let's hence measure our own guranteed one.
creds-util: only lock against public key PCR stuff if we are booted with UEFI supporting TPMs
The UKI public key PCR stuff only works if we get PCR measurements from
the pre-boot environment, hence automatically disable the logic by
default if we don't have that.
pcrlock: deal with firmwares which understand TPM but where no TPM is available
This is a potentially common case in VMs: firmwares might know the
concept of TPMs, but the hardware is not enabled in the specific VM.
Let's handle this case nicely.
So far we always conditioned our TPM magic on the UKI having detected
TPM support in the firmware. This is a bit limiting when we want to
support a software TPM that is not visible to the firmware. Hence let's
split this up, and add a separate control that can be set via the kernel
command line. However, as before, let's by default inherit the firmare
TPM discovery state into it, to retain the current behaviour unless
overriden.
With this in place, boot with "systemd.tpm2_measured_os=1
systemd.tpm2_software_fallback=1" on the kernel cmdline to get the swtpm
fallback and then a measured OS based on it.
tree-wide: relax TPM available checks for many cases
In many cases it's essential to know if the firmware supports a TPM, but
in others we should accept it if the firmware doesn't have TPM support,
in particular if we want to run the OS with a software TPM.
Hence, add tpm2_is_mostly_supported() as function similar to
tpm2_is_fully_supported(), with the only difference that the former
doesn't insist on a firmware supported TPM. Then, change a number of
users over to this (but not all).
tpm2-generator: if requested run things with an swtpm
We want to start the software TPM fallback only if no real hw is
evailable and if the user opts-in to this behaviour. Add a generator
that drives all this, based on kernel command line configuration.
tpm2: add "systemd-tpm2-swtpm" wrapper for "swtpm"
For TPM-less systems it's sometimes valuable to have a fill-in software
TPM running from early boot on, so that TPM-based functionality can
"just work" and rely on TPM semantics, even if it's at a substantially
weaker security level.
This adds a wrapper around swtpm. It's a binary that chainloads swtpm
but does a few preparatory steps and integrates into systemd's logic
otherwise.
All this is then exposed as systemd-tpm2-swtpm.service.
The service is not hooked into much yet, that is added in later commits.
Luca Boccassi [Thu, 26 Mar 2026 15:07:56 +0000 (15:07 +0000)]
labeler: limit file-based label to 5
When doing large refactors or large changes the bot spams
labels left and right, making the PR unreadable. Use the
new option to limit the bot to a max of 5 file-based
labels. If more than 5 would be set, all file-based labels
are skipped.
Mike Yuan [Wed, 25 Mar 2026 15:00:14 +0000 (16:00 +0100)]
creds: if newline is explicitly requested, skip tty check
Before this commit, the > 0 state of arg_newline tristate is
simply ignored.
Yes, this is a minor compat break, but I'd argue the previous
behavior was not useful as "yes" is treated the same as "auto".
An issue also reported that it was quite surprising.
Michael Vogt [Thu, 26 Mar 2026 12:25:02 +0000 (13:25 +0100)]
core: drop incorrect comment about SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)
The comment about `SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)` is
incorrect. To quote @YHNdnzj:
```
nah, the capability-based permission model is a legacy from kdbus. We cannot do it race-freely without it. Please simply drop the comment.
```
The manager_do_set_objective() was doing a bunch of work to
check the `root` path that can already be done via
`json_dispatch_path` so instead of duplicating use the helper.
gpt-auto-generator: generate an initrd ESP mount if it makes sense
We need to store state persistently for the software TPM (i.e. the root
key). But given that TPMs are generally used to unlock the rootfs, this
storage cannot be on the rootfs. Hence let's use the ESP instead, as the
next best thing, that is guaranteed to exist during early boot, given we
just were booted from it.
This defines automatic logic for this, but does not cause the ESP mount
job to be enqueued (since typically we don't actually want that
mounted), this is left for the actual services that needs to be done.
Note that the mount here is set up quite differently from the one from
the host: since initrds are short-lived anyway, it seemed pointless to
use autofs. Moreover this uses a fixed place to mount the ESP, inspired
by the /sysroot/ + /sysusr/ mount naming. All that to simplify things a
bit for the consumers (which is mostly swtpm)
Cynthia [Tue, 17 Mar 2026 22:30:31 +0000 (23:30 +0100)]
kernel-install(uki): filter comments from cmdline
This change aligns the behaviour of UKI generation with the behaviour
of BLS. The latter filters out lines starting with a #, allowing users
to add comments and/or temporarily remove some flags from the kernel
command line.
The kernel-install test have been adjusted to use a multiline cmdline
with a comment in it. Without this patch, the test fails.
This adds a new cloud IMDS client, that can cover AWS, Azure, GCP,
Hetzner imds to varying degrees. Each cloud has this and it's very basic
functionality, hence I think it makes sense to add this to systemd.
Since the clouds are all different this tries hard to do the abstract
common logic in code, but encode the endpoint details, and well-known
keys in hwdb, attached to the DMI id device.
Why all this?
* Efficiency: we can schedule this in the initrd, at the earliest points
possible, without unnecessary delays
* Robustness: imds is typically slow and/or heavily rate-limited:
systemd-imdsd as single entrypoint can deal with that, and provide a
reliable, cached interface
* Security: the idea is that systemd-imdsd is the only service behing
able to access the IMDS HTTP, and the host carries a blackhole route for
it otherwise. That way sensitive info can be kept away from clients, and
requires polkit auth for access
* Simplicity: extraction of systemd's system credentials from IMDS
userdata happens with systemd's own infra, and for many usecases that
should already be enough.
* Measurements: before accepting the IDMS userdata, it can be measured
into a PCR, as any other configuration input for the system
Daan De Meyer [Thu, 26 Mar 2026 12:33:38 +0000 (12:33 +0000)]
ci: Support multi-line review comments in claude-review
Pass side, start_line, and start_side through to createReviewComment()
when present, enabling multi-line review comments. Update the prompt to
document all positioning fields using JSON Schema and make line required.
They document it here:
https://octokit.github.io/rest.js/v22/#pulls-create-review-comment
but apparently that's out of date and this doesn't work anymore.
Luca Boccassi [Fri, 13 Mar 2026 01:52:12 +0000 (01:52 +0000)]
boot: add checks for invalid splash images in UKI
A malformed bmp with 8bits depth but smaller color
map would cause out of bounds reads. This is not a real
problem as the image is signed, but better to be safe.
imds: add generator that hooks in IMDS logic on cloud guests
The infrastructure added in the previous commits added support for IMDS
client functionality, but didn't really to enable the logic by default
on suitable hosts.
This commit adds a generator that automatically hooks the IMDS
functionality into the boot process if it detects that the system is
running on a compliant cloud system. it enables both the imds daemon and
the client.
This automatically measures the IMDS 'userdata' into PCR 12, i.e. where
we measure the other owner-supplied configuration, such as confexts and
credentials and similar.
(Why 12? It's really about who owns the data and what it is for.
PCRs/NvPCRs are scarce hence there's a strong incentive to not go
overboard with new allocations, and IMDS userdata in purpose and owner
is very very similar to confexts and credentials, hence let's reuse the
PCR for this purpose.)
imds: add "systemd-imds" tool that is a simple client to "systemd-imdsd"
This is a client tool to the systemd-imdsd@.service added in the
previous commit. It's mostly just a 1:1 IPC client via Varlink. It can
be used to query any IMDS key, but it's primary usecase is to acquire
the "userdata" from IMDS. Moreover, if invoked with the --import switch
it will check if the userdata contains a list of system credentials. If
so, it will import them into the local credstore. If the userdata does
not look like a list of system credentials no operation is executed,
under the assumption the data is intended for cloud-init instead.
It also imports a couple of other fields, if available and recogniuzed,
such as SSH keys and the hostname.
imds: add new systemd-imdsd.service that makes IMDS data accessible locally
This service's job is to talk to a VM associated IMDS service provided
by the local Cloud. It tries to abstract the protocol differences
various IMDS implementations implement, but does *not* really try to
abstract more than a few basic fields of the actual IMDS metadata.
IMDS access is wrapped in a Varlink API that local clients can talk to.
If possible this makes use of the IMDS endpoint information that has
been added to hwdb in the preceeding commit. However, endpoint info can
also be provided via kernel command line and credentials. For debugging
purposes we also accept them via environment variables and command line
arguments.
This adds a concept of early-boot networking, just enough to be able to
talk to the IMDS service. It is minimally configurable via a kernel
cmdline option (and a build-time option): the user may choose between
"locked" and "unlocked" mode. In the former mode direct access to IMDS via
HTTPS is blocked via a prohibit route (and thus all IMDS communication
has to be done via systemd-imdsd@.service). In the latter case no such
lockdown takes place, and IMDS may be acquired both via this new service
and directly. The latter is typically a good idea for compatibility with
current systems, the former is preferable for secure installations.
This adds a hardware database that contains information about IDMS
functionality of various clouds, keyed off the SMBIOS identification of
each. Currently this contains information about 6 major clouds, but the
idea is that this grows to include more and more major clouds.
Nothing uses this data yet, that's added in a later commit.
shared/table-format: generalize table_sync_column_width to more columns
The column index is moved to the first position. I think we're unlikely
to want to synchronize widths of *different* columns, and having just
one column argument makes the callers simpler. Also, the type is changed
to size_t to match other functions, and this avoids the need to cast
to size_t in the callers.