When placing new partitions and there's space left because the new partitions
aren't occupying the whole free space, context_grow_partitions_on_free_area()
is supposed to distribute the free space between the new partitions. If still
no partition wants the free space, the free space ends up becoming padding.
Currently that padding is allocated to the partition preceding the FreeArea
(ie. a->after). This obviously means that the new partitions now end up at
the *end* of the free area rather than at the beginning, which is somewhat
unexpected given how partition placement usually is done.
Fix it by finding the last partition that belongs to the free area, and then
allocating the padding to that partition, so that the new partitions end up
getting aligned with the beginning of the free area, not the end.
Because the span might not be rounded to grain if there's a pre-allocated
a->after partition before the free area, we need to round it down ourselves
(otherwise the "left >= p->new_padding" assertion in context_place_partitions()
is going to fail).
Also ensure the fix works as expected by adding a test.
`tpm2_get_best_pcr_bank()` only ever considered the SHA256 and SHA1
banks (both the `LoaderTpm2ActivePcrBanks` path and the capability
guesswork). On a TPM whose only active bank is SHA384 it returned
`-EOPNOTSUPP`, breaking sealing/enrollment (cryptenroll, credential
encryption, legacy unseal). The restriction looks like a historical
simplification — `efi_get_active_pcr_banks()` already decodes
SHA384/SHA512 and `tpm2_hash_algorithms[]` already lists them.
This PR introduces an explicit preference table (SHA256 > SHA512 >
SHA384 > SHA1) and selects from it. SHA256 stays the top preference for
backwards compatibility, so existing systems keep using the same bank
and the legacy unseal-guess in `tpm2_unseal()` stays consistent;
SHA384/SHA512 are only chosen when SHA256 is unavailable, SHA1 remains
the last resort.
Behavior for existing SHA256/SHA1 systems is unchanged. Includes a unit
test for the bank-preference logic.
Related to https://github.com/systemd/systemd/pull/42537
UEFI firmware type hwids must be matched against the current hardware first.
This change implements that. Additionally, some extra validations on the hwids
entries have also been added.
libnss_systemd currently uses a thread_local recursion guard to
avoid re-entering nss-systemd during NSS lookups.
Since libnss_systemd.so.2 is loaded lazily by glibc, accessing ELF TLS
may trigger dynamic TLS allocation in __tls_get_addr(). Under allocation
failure conditions, glibc terminates the process from the dynamic loader
instead of allowing the NSS module to return a normal failure.
Replace the recursion guard with POSIX thread-specific data to preserve the
same per-thread semantics while avoiding ELF TLS in the NSS module.
Note that pthread_setspecific() may still allocate internally on first use
per thread. The key improvement is that any such failure is returned
as a normal error code rather than terminating the process from inside
the dynamic loader.
Related: #42559
For a good user experience, users expect to be informed of how many
attempts they have before being locked out of their FIDO2 device.
By displaying such information in advance, the user can make strategy to
obtain the accurate PIN or wait when they are close to an authority who
can provide them for a recovery key.
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
We already support exponential backoff for automatic restarts via
RestartSec=/RestartSteps=/RestartMaxDelaySec=, but there is no way to
randomize the restart delay. When many instances of a service fail at
the same time (e.g. because a shared resource briefly went away) they
are all restarted in lockstep, creating a thundering herd problem.
So this commit adds a simple `RestartRandomizedDelaySec=` service
option which is similar to the timer `RandomizedDelaySec=` and
adds a randomized restart delay.
Fedora has switched to openssl 4, and we generate a Requires dependency
on libcrypto for the systemd-udev subpackage, so preferring openssl-3
does the wrong thing. So the order in the dlopen note needs to be switched.
But in general, we want to get rid of openssl-3, so we want to load
openssl-4 in preference. Change the order in both places.
(The "compat" order can stay in 261-stable for other distros.)
Two PRs got merged at the same time, which cause a test to fail,
as they work individually but fail when combined
TEST-74-AUX-UTILS.sh[1688]: + /usr/lib/systemd/systemd-report generate io.systemd.Manager.UnitsTotal
TEST-74-AUX-UTILS.sh[1805]: {"mediaType":"application/vnd.io.systemd.report","metrics":[{"name":"io.systemd.Manager.UnitsTotal","value":249}],"timestamp":"Fri 2026-06-19 19:50:48 UTC"}
TEST-74-AUX-UTILS.sh[1806]: + /usr/lib/systemd/systemd-report generate io.systemd.Manager.UnitsTotal
TEST-74-AUX-UTILS.sh[1807]: + jq .
TEST-74-AUX-UTILS.sh[1807]: {
TEST-74-AUX-UTILS.sh[1807]: "mediaType": "application/vnd.io.systemd.report",
TEST-74-AUX-UTILS.sh[1807]: "metrics": [
TEST-74-AUX-UTILS.sh[1807]: {
TEST-74-AUX-UTILS.sh[1807]: "name": "io.systemd.Manager.UnitsTotal",
TEST-74-AUX-UTILS.sh[1807]: "value": 249
TEST-74-AUX-UTILS.sh[1807]: }
TEST-74-AUX-UTILS.sh[1807]: ],
TEST-74-AUX-UTILS.sh[1807]: "timestamp": "Fri 2026-06-19 19:50:48 UTC"
TEST-74-AUX-UTILS.sh[1807]: }
TEST-74-AUX-UTILS.sh[1688]: + /usr/lib/systemd/systemd-report upload --url=http://localhost:8089/
TEST-74-AUX-UTILS.sh[1808]: Failed to normalize report JSON: Wrong medium type
https://github.com/systemd/systemd/pull/42594https://github.com/systemd/systemd/pull/42595
Disable normalization for now, and track the issue at
https://github.com/systemd/systemd/issues/42669
Follow-up for 3c2f7c6002
The stub copies the embedded kernel PE into an xmalloc_pages()
allocation with AllocateAnyPages, which only guarantees EFI_PAGE_SIZE
(4 KiB) alignment.
The arm64 kernel declares SectionAlignment = SZ_64K. When _text is not
64K-aligned the kernel's EFI stub reallocates and copies the image,
which can fail with EFI_OUT_OF_RESOURCES on memory-constrained firmware
(e.g. U-Boot, bounded by CONFIG_SYS_MALLOC_LEN rather than full DRAM).
The failure is non-deterministic since AllocateAnyPages returns whatever
page the allocator finds first.
Plumb SectionAlignment through pe_kernel_info() and allocate via
xmalloc_aligned_pages(). For alignment <= EFI_PAGE_SIZE (e.g. x86_64) it
reduces to a plain AllocatePages(), so the change is free there.
SectionAlignment comes from the PE header, which the spec requires to be
a power of 2. Sanitize it in pe_kernel_info() right where it is parsed:
a non-conforming value falls back to plain page alignment (matching the
behavior of older systemd-stub versions that ignored the field) rather
than propagate something that would break xmalloc_aligned_pages()'s
over-alignment maths, with a log_warning() so the fallback stays
diagnosable.
Co-developed-by: Claude <claude@anthropic.com>
Closes: #42443
Reported-By: Agathe Porte <agathe.porte@oss.qualcomm.com>
Tested-By: Agathe Porte <agathe.porte@oss.qualcomm.com>
Up to now we recommended to use TARGET.upholds/ symlinks to start units
when an extension is loaded. However, this has some drawbacks. First,
for services that should not be tried to be started all the time we have
to resort to hiding them through a target that gets uphold and then
uses regular .wants/ for the actual service. Second, we actually leak
services on extension unload even if the unit has disappeared with the
extension. Third, to affect a service through a drop-in or a config
change from a confext/sysext and that service is already running, we
need a way to restart/reload it instead of just starting it.
Similar to EXTENSION_RELOAD_MANAGER=1, add a EXTENSION_RESTART_UNITS=
and a EXTENSION_RELOAD_OR_RESTART_UNITS= setting to the
extension-release metadata file, carrying a whitespace-separated list
of units to restart/reload on merge/refresh/unmerge after the daemon
reload. Also detect when the unit has vanished which is normally the
case when the unit was part of the unmerged extension, and stop it
explicitly to prevent it leaking. When the extension itself ships the
binary it should use EXTENSION_RESTART_UNITS= to make sure the new
binary is picked up. Since starting through this setting does not work
when the extension is mounted from the initrd, extensions should still
ship at least a .wants/ symlink to start at boot but can also continue
to ship a .upholds/ symlink for backwards compatibility without any
drawback and still benefit from the unit stopping triggered by the new
setting. While there are cases where one could want to set
EXTENSION_RESTART_UNITS= without requiring a daemon reload (e.g., an
env var file change instead of a unit drop-in), we now do an implicit
daemon reload when we have to restart units so that we know we work on
the right state and we spare users remembering to set this setting in
addition to prevent running into this issue.
This PR fixes userdb lookups for NSS users that are resolved through an
alias but returned with a canonical user name.
Some NSS providers, such as SSSD, can successfully resolve a user by an
alias-like name, for example a Kerberos/AD UPN (for example
testuser@example.test) while returning a passwd record with the
canonical login name.
The original lookup name was not preserved. Later, the userdb worker
checked whether the returned record matched the requested name with
user_record_matches_user_name(). Since the requested name was
testuser@example.test, but the record only contained testuser, the
lookup was rejected as:
`io.systemd.UserDatabase.ConflictingRecordFound`
This also caused pam_systemd to fail opening sessions for such users
with:
`pam_systemd(...:session): Failed to get user record`
In my case, this broke graphical logins for Samba users logging in with
a UPN on systems where SSSD canonicalizes the NSS result.
My solution preserves the requested name as a UserRecord alias when an
NSS lookup by name succeeds but the returned pw_name differs from the
requested name.
This allows user_record_matches_user_name() to accept the canonicalized
NSS result instead of treating it as a conflicting record.
The patch does not invent new aliases. It only records the name that NSS
itself already accepted and resolved successfully.
I'm not an expert on working with systemd, so I'm asking for a review of
my PR.
The ltype parameter for SizeMinBytes and SizeMaxBytes in the config
parsing table was reversed. SizeMinBytes should round UP (ltype > 0)
to ensure the partition is at least the specified size, while
SizeMaxBytes should round DOWN (ltype < 0) to ensure the partition
doesn't exceed the specified size.
This matches the documentation in repart.d.xml which correctly states:
- SizeMinBytes: 'rounded upwards'
- SizeMaxBytes: 'rounded downwards'
The same fix is applied to PaddingMinBytes and PaddingMaxBytes which
share the same config_parse_size4096 parser.
Fixes: #42526
Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Liu Zhangjian <liuzhangjian@uniontech.com>
Fixes#42330
The `pending` and `reboot` verbs, and the `--reboot` switch, decide
whether to reboot by comparing the newest installed version against the
booted OS version (`IMAGE_VERSION=` from os-release). When a component
is selected via
`--component=`, this ends up comparing the component's version against
the unrelated host OS version; by design these live in separate version
spaces, so the comparison is meaningless and reboot decisions become
arbitrary: depending on the relative version strings, sysupdate either
always reboots or never does.
### Example from the issue:
% /usr/lib/systemd/systemd-sysupdate --component=containerd reboot
Newest installed version '2.3.0' is older than booted version
'20260527200656'.
This refuses the combination with a clear error instead of silently
performing a bogus comparison:
- `verb_pending_or_reboot()` rejects `--component=` for the
`pending`/`reboot` verbs (mirroring the existing `--root=`/`--image=`
rejection).
- `verb_update_impl()` rejects `--reboot` combined with `--component=`,
before any update work is done.
Correctly tracking a per-component "booted" version (which could be
"none", making a reboot always apply) is a larger feature and left for
the future, as the reporter suggested.
The daemon (`sysupdated`) and `updatectl` don't perform this
host-version comparison, so the change is confined to the
`systemd-sysupdate` CLI.
Documentation and a negative regression test (TEST-72) are included.
### AI-use Disclosure:
I took assistance from Claude Opus 4.8 to scope out the issue, help with
writing proper comments/documentation, and help with writing the PR
description.
Move the --reboot/--component= rejection into parse_argv() alongside the
other cross-option checks, and tighten TEST-72 to assert the specific
guard message rather than merely a non-zero exit.
The `pending` and `reboot` verbs, as well as the `--reboot` switch, compare
the newest installed version against the booted OS version (IMAGE_VERSION= from
os-release). When a component is selected via --component=, this compares the
component's version against the unrelated host OS version, which by design live
in separate version spaces. The result is arbitrary reboot decisions: depending
on the relative version strings sysupdate would either always or never reboot.
Refuse the combination with a clear error instead of silently performing a
bogus comparison. Correctly tracking a per-component booted version is left as a
future feature.
Fixes: https://github.com/systemd/systemd/issues/42330
run0 doesn't actually use the auth pam stack, since polkit does the
requisite authorization. However, if the service type is left undefined
pam falls back to the definitions of the "other" service, which, at
least in Arch Linux but possibly more, includes pam_warn.so to notify
the user about this apparent error.
This creates a bit of logspam, as systemd does actually call pam_setcred
in its generic pam code, which depends on the auth pam stack, creating a
warning message in the journal on every invocation of run0.
pam_deny.so is a no-op, which avoids falling back to the other pam
service.
When multiple swap devices exist, prefer one with enough free space
to hold the hibernation image over one that cannot, regardless of
priority. If no swap can fit, fall back to priority-first selection.
This avoids deterministically failing hibernation when the
highest-priority swap is too small but a lower-priority one fits.
Signed-off-by: dongshengyuan <dongshengyuan@uniontech.com>
Co-developed-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This adds the following:
1. systemd-report gains a new --sign= option, taking a boolean. If true,
this makes systemd-report generate + systemd-report upload generate a
signed report, instead of a regular one. The signatures are collected
from Varlink-based backends.
2. One such backend is added which does a simple Ed21159 based signing
scheme.
3. this adds a new metrics source which just reports text files
symlinked into a special dir as metrics. This is used to report the
Ed21159 public key as metric, by default, if it exists.
4. finally, systemd-report itself is turned into a varlink service. this
is useful for example for extracting a report from a system coming in
via the varlink/http bridge.
I thought a long time about the format of signing of reports. Initially
i intended to do this like homed's user record signing, i.e. require
normalization of the record, then normalize the record, and write it out
in dense form, since the result. Finally insert the resulting hash into
the user record itself. People have pointed me to the inherent messiness
of signing JSON this way though, as it requires any participant that
wishes to sign/authenticate records this way to implement the exact same
normalization/formatting rules, and in particular in the area of
floating point numbers (of which metrics presumably will have many) this
is quite problematic.
This signing hence goes a different way. instead of expecting
signer+verifier to independently come to the same normalized text form
of the json data, let's instead output a JSON-SEQ sequence, where the
first object is the report, and any subsequent objects are one signature
each. the signatures are supposed to cover the precise binary
representation of the first element in the JSON-SEQ stream. (i.e. from
the RS to the NL).
or in other words: a verifier would receive the JSON-SEQ stream, split
it up before each RS. Then it would leave object 1 unparsed for the
moment, and parse objects 2…n. It would then authenticate object 1's
precise binary representation with objects 2…n. Once that checks out, it
would parse object 1, and use it as report.
Since a1f4fd3876 udev processes
run in an 'udev' subcgroup, to avoid killing control processes
when clearing workers. But the main process is still in the same
cgroup, so the atomic cgroup.kill cannot be used.
If the main subcgroup exists, try to create a sibling 'workers'
cgroup, and use it for workers processes, and use cgroup.kill if
available.
This is especially useful as rules can spawn arbitrary programs/scripts
and TasksMax= is set to unlimited.
Acquiring a LUO session from /dev/liveupdate requires privileges, and
also the device is a single-owner driver so only a single process can
open it at any given time.
Add a LUOSession= service settings that allows units running without
privileges to get a session assigned to them.
The kernel imposes a 64 chars limit on session names, which is too short
to avoid clashes, so derive a hash from joining the unit name with the
parameter name, that way two units using the same setting don't clash.
IMDS access requires networking, hence we need to run after
network-online.target. Everything else would be racy and result in
likely timeouts, because we might try to contact the network too early.
This reverts commit 3f33f4d057.
This fixes 2 issues with `ukify`:
`config_list_prepend` needs a list. The 2 more used entries are
specially treated (parsed into a list) and therefore happen to work, but
trying to set `UKI/Firmware` throws a type error, as it is passed as
just a string.
The `UKI/SBAT` config entry is a list but used the default `config_push`
value which sets it if unset (which also failed since its default of
`[]` is not `None`) - change this to `config_list_prepend`.
I did consider adding a `config_list_append`, but as far as I'm aware,
the order of sbat entries should not change the outcome anyway.
OpenSSL 4 broke ABI, so we need to look for both SONAMEs.
Try libcrypto.so.3 first, and fallback to libcrypto.so.4,
so that the older and more stable version is used if both
are installed, giving distros time to fix regressions.
Cover the full preference order (SHA256 > SHA384 > SHA512 > SHA1), the
single-bank cases and the -EOPNOTSUPP path. Also test the _legacy()
variant: a set with SHA384/SHA512 active but no SHA256 must resolve to
SHA1, not SHA384, pinning down the backwards-compatibility behavior that
keeps legacy sealed secrets unsealable.
Co-developed-by: Claude Opus 4.8 <noreply@anthropic.com>
tpm2_get_best_pcr_bank() only ever considered SHA256 and SHA1, both in
the firmware-reported LoaderTpm2ActivePcrBanks path and in the
capability-based guesswork. On a TPM whose only active bank is SHA384 it
returned -EOPNOTSUPP, which for units ordered before sysinit.target
(systemd-pcrextend/systemd-pcrproduct.service) propagates and hangs the
boot, even with --graceful (that only covers "no TPM present").
Introduce a preference table (SHA256 > SHA384 > SHA512 > SHA1) and select
from it. SHA256 stays top for backwards compatibility; SHA384 is
preferred over SHA512 as it produces a shorter digest and so uses less of
the bounded TPM event log; SHA1 is the last resort. SM3/SHA3 are excluded
as we cannot compute those digests in software.
The legacy unseal path in tpm2_unseal() re-derives the bank for old
enrollments that did not record one (pcr_bank == UINT16_MAX). Those were
sealed by code that only picked SHA256 or SHA1, so the wider preference
could now re-derive a stronger bank the secret was never bound to and
silently fail to unseal. To stay compatible, the selection takes the
preference list as a parameter, and a _legacy() variant restricted to
{SHA256, SHA1} is used for that path; fresh-sealing callers keep the full
preference.
Co-developed-by: Claude Opus 4.8 <noreply@anthropic.com>
The binary catalog reader trusted two values straight from a (possibly
hostile) database: open_mmap() summed header_size + n_items *
catalog_item_size in uint64 with no overflow check, and find_id() added
the matched item's offset to the map base with no upper bound. Reachable
through sd_journal_get_catalog() with $SYSTEMD_CATALOG set, this let
catalog_get()/catalog_list() strdup() a string starting outside the
mapping. Guard the size math with MUL_SAFE/INC_SAFE and reject item
offsets that fall outside the file.
A single incorrect password attempt from the user would be tried
twice, because the first failed try would think it was the
try_discover_key method that failed, and would invalidate that instead
of the actual failed password. This resulted in the user being
prompted one fewer time than they should be.
Fixes#38495