A `\u` escape in `cunescape_one()` was accepted for any 16-bit value
except NUL. That range `0x0000`-`0xFFFF` includes the UTF-16 surrogates
`0xD800`-`0xDFFF`, which are not valid Unicode scalar values and cannot
be encoded as valid UTF-8.
(cherry picked from commit eb03267070)
Workers may return a newer embedded identity while processing
an owner update. Accept self-modifiable changes, but require
privileged changes to carry a trusted signature.
Follow-up for 70a5db5822
(cherry picked from commit 765dc96746)
The ExitType= documentation stated that ExitType=main "cannot be used
with Type=oneshot", which is incorrect: ExitType=main is the default
and works fine with Type=oneshot. The restriction actually enforced by
the code is that ExitType=cgroup is refused for Type=oneshot services
(see service_verify() in src/core/service.c).
Move the "cannot be used with Type=oneshot" note from the ExitType=main
bullet to the ExitType=cgroup bullet where it belongs.
Fixes#42327
Signed-off-by: Liu Zheng <liuzheng@uniontech.com>
(cherry picked from commit 3ac01c2512)
The normal order of operations when iterating the GLib main loop is:
- prepare() -> sd_event_prepare()
- poll the file descriptors
- check() -> sd_event_wait()
- dispatch() -> sd_event_dispatch()
GLib does not guarantee that check() is always called between two
consecutive prepare() invocations. When another thread attaches or
removes a source with poll fds while the main thread is inside
poll(), g_main_context_check_unlocked() returns immediately without
calling any source's check() callback.
Since the sd_event GSource adapter maps prepare/check/dispatch to
sd_event_prepare/sd_event_wait/sd_event_dispatch, a skipped check()
leaves sd_event in ARMED state. The next prepare() then hits
assertion "e->state == SD_EVENT_INITIAL".
The following program reproduces the faulty scenario by creating a
thread that attaches a new fd during the poll phase:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <glib.h>
#include <systemd/sd-event.h>
#include <unistd.h>
/* from glib-event-glue.c */
extern GSource *g_sd_event_create_source(sd_event *event);
static gpointer thread_func(gpointer user_data) {
GSource *source;
GPollFD pollfd;
int fd;
g_usleep(G_USEC_PER_SEC / 10);
fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (fd < 0)
abort();
source = g_source_new(&(GSourceFuncs){0}, sizeof(GSource));
pollfd.fd = fd;
pollfd.events = G_IO_IN;
pollfd.revents = 0;
g_source_add_poll(source, &pollfd);
g_source_attach(source, NULL);
g_source_destroy(source);
g_source_unref(source);
close(fd);
return NULL;
}
int main(int argc, char *argv[]) {
sd_event *event = NULL;
GSource *source;
GThread *thread;
int i, r;
r = sd_event_default(&event);
if (r < 0)
return 1;
source = g_sd_event_create_source(event);
if (!source)
return 1;
g_source_attach(source, NULL);
for (i = 0; i < 50; i++) {
thread = g_thread_new("check-skip", thread_func, NULL);
g_main_context_iteration(NULL, TRUE);
g_thread_join(thread);
g_main_context_iteration(NULL, FALSE);
}
g_source_destroy(source);
g_source_unref(source);
sd_event_unref(event);
return 0;
}
When the program uses the current glue code, it crashes with:
Assertion 'e->state == SD_EVENT_INITIAL' failed at src/libsystemd/sd-event/sd-event.c:4560, function sd_event_prepare(). Aborting.
Fix the problem by skipping sd_event_prepare() if the event is
already ARMED: it was already prepared in a previous iteration and
it can be polled immediately.
(cherry picked from commit 9af2d7e4c8)
The default IMDS network mode was changed to unlocked, but the
systemd-imdsd@.service documentation still described locked as the
default.
Fixes#42687
(cherry picked from commit ac64b2dfa3)
This partially reverts 13ce62caba.
Sometimes, the kernel passes non-safe characters in uevent files,
such as the UNIQ= property for USB serial devices.
Instead of rejecting the entire uevent file, let's only ignore the
specific key-value pairs that contain non-safe characters.
Fixes#43008.
(cherry picked from commit 8eede7122f)
Add Sony PlayStation controller entries to 70-sound-card.hwdb so that their
ALSA sound devices are tagged with SOUND_FORM_FACTOR=controller:
- DualSense (054c:0ce6)
- DualSense Edge (054c:0df2)
- DualShock 4 CUH-ZCT1x (054c:05c4)
- DualShock 4 CUH-ZCT2x (054c:09cc)
These controllers expose USB audio but are neither headsets nor speakers.
Pinning them in the hwdb ensures they are identified correctly before any
fallback matching occurs.
(cherry picked from commit 3bc3b6c6e7)
The modern hwdb.bin format contains the filenames of the input data that
makes up the database. This is useful but in offline builds where
--root is used, the filenames are the full build paths including the
specified root. This introduces build paths and thus information
leakage and non-reproducible data.
Solve this by stripping the root prefix off the original path when
passing to import_file.
Add TEST-17-UDEV.hwdb.sh to verify that hwdb.bin files contain the
path inside the root, but not the path of the root.
(cherry picked from commit ab9be70e94)
bus_init_api() issued a synchronous GetId call on every API bus
(re-)connection to decide whether saved subscription state could be
coldplugged onto the new connection.
If the D-Bus socket unit is listening while the message bus daemon
behind it is gone, connect() succeeds against the socket backlog but
nothing answers the authentication handshake. The synchronous call
then blocks PID 1 for BUS_AUTH_TIMEOUT (90 seconds by default), and
queued bus operations can trigger repeated reconnection attempts.
This was observed during shutdown as roughly 15 minutes of teardown
progressing only in 90-second intervals.
Query the instance ID asynchronously on every connection. Defer API
setup until the reply is processed, so saved subscriptions are
validated and coldplugged before new subscription requests can arrive.
If the query cannot be queued or its reply is invalid, discard the
unvalidated state and expose the API without blocking the manager.
Reset the live bus ID on every connection and serialize pending bus ID
and subscription state across reload and reexec. During daemon-reload,
preserve state that was already awaiting the asynchronous reply while
discarding the duplicate state produced by the reload itself.
Also remove the now-unused synchronous bus_get_instance_id() helper.
(cherry picked from commit 1166f4472d)
Add a zero-length test for offset > max so the first bounds guard
is covered separately from the length overflow check.
Follow-up: 231857cfe8
(cherry picked from commit 4368db3219)
suggest_passwords() references the N_SUGGESTIONS macro, which is defined in
password-quality-util.h. Commit ff33c8f87d ("Extend test-dlopen-so to also
cover cases when built without support") introduced the per-backend split
headers: for the pwquality backend it added the new
password-quality-util-pwquality.h include while keeping password-quality-util.h,
but for the passwdqc backend it replaced password-quality-util.h with
password-quality-util-passwdqc.h (which only pulls in shared-forward.h). As a
result N_SUGGESTIONS is no longer declared in the passwdqc translation unit and
the build fails when the passwdqc backend is enabled.
The passwdqc backend is not exercised by the default CI, so this went
unnoticed. Add the include back, matching the pwquality backend.
(cherry picked from commit f9a9b6b4f5)
Reject credentials whose not-after timestamp is equal to the timestamp
at each encryption entry point.
Reproducer: systemd-creds --timestamp=TS --not-after=TS encrypt in out
Before: encryption succeeded, but decrypt refused the credential as not
in order.
Follow-up: 21bc0b6fa1
(cherry picked from commit 7a81ad4b6b)
Use the image metadata size when building JSON output, matching the
value already shown by the text output.
Reproducer: systemd-dissect --json=short image.raw
Before: text output showed Size, but JSON omitted the top-level size
field.
Follow-up: be5bee2a13
(cherry picked from commit dab2a82a30)
Skip generated fstab and crypttab output when --dry-run=yes is used,
after confirming there is eligible content to generate.
Reproducer: systemd-repart --dry-run=yes --generate-fstab=/tmp/root/etc/fstab -
Before: the command exited successfully and still wrote the requested
fstab file.
Follow-up: 1a0541d44c
(cherry picked from commit e7e68ec449)
Validate passwd.shell.root after reading the credential so it uses
the same target-root shell checks as --root-shell= and prompted input.
Reproducer: CREDENTIALS_DIRECTORY=... systemd-firstboot --root=...
with passwd.shell.root=/bin/nonexistentshell
Before: /etc/passwd was written with the nonexistent root shell and
the command exited successfully.
Follow-up: 416f7b3a11
(cherry picked from commit 50eaa51ce1)
Validate passwd.shell.<user> credentials with the same login-shell
rules used for sysusers.d shell fields before writing passwd entries.
Reproducer: CREDENTIALS_DIRECTORY=... systemd-sysusers --root=...
with --inline 'u creduser 999 "Cred User" / -'
Before: relative-shell was written as the login shell and the command
exited successfully.
Follow-up: 99e9f896fb
(cherry picked from commit 05b49871a2)
Removed note clarifying that portable services are only for system services and not user services, and changed comparisons to "system services" with just "services". With newer systemd versions, `systemd-portabled` can be run as a user service.
(cherry picked from commit bb3fcd73ce)
[1177/3647] Compiling C object systemd-networkd.p/src_network_networkd-bridge-vlan.c.o
In function ‘bridge_vlan_append_set_info’,
inlined from ‘bridge_vlan_set_message’ at ../src/network/networkd-bridge-vlan.c:257:21:
../src/network/networkd-bridge-vlan.c:162:28: warning: ‘untagged’ may be used uninitialized [-Wmaybe-uninitialized]
162 | if (untagged == u)
| ^
../src/network/networkd-bridge-vlan.c: In function ‘bridge_vlan_set_message’:
../src/network/networkd-bridge-vlan.c:111:14: note: ‘untagged’ was declared here
111 | bool untagged, pvid_is_untagged;
| ^~~~~~~~
(cherry picked from commit d094067547)
Reject --app-specific= for the new verb. The option only makes sense
for verbs that derive IDs from an existing base ID.
Reproducer: systemd-id128 new --app-specific=4f68bce3e8cd4db196e7fbcaf984b709
Before: the command printed a random ID and exited successfully.
Follow-up: 0d1d512f7f
(cherry picked from commit 8729fa0e4b)
When a unit is stopped we try to destroy the credential mount point,
which might not exist. This logs at error level, which is noisy and
pointless. Downgrade to debug level on ENOENT.
systemd[1]: Failed to determine whether '/run/credentials/run-p119516-i119816.service' is the root file system: No such file or directory
Follow-up for 9dd2dab37e
(cherry picked from commit b9ef5951f8)
When systemd-sysinstall probes whether an installation would fit by
calling io.systemd.Repart.Run() in dry-run mode, systemd-repart runs as
a child process sharing sysinstall's stderr. When the requested
partitions didn't fit, context_ponder() logged its failure at LOG_ERR
before vl_method_run() converted it into a structured Varlink error
(InsufficientFreeSpace or DiskTooSmall), which the client then reports
to the user in its own words. The internal message hence appeared
interleaved with the user-facing report:
Can't fit requested partitions into available free space (1.9G), refusing.
The selected disk is not large enough for an OS installation.
The size of the selected disk is 0B, but a minimal size of 15.7G is required.
Log at LOG_DEBUG when running as a Varlink service, and keep LOG_ERR
for CLI invocations, where this message is the primary error report
shown to the user.
(cherry picked from commit 70cc332640)
The io.systemd.Repart interface documents the currentSizeBytes field as
the size of the selected block device, both in the Run() method's
dry-run reply and in the InsufficientFreeSpace and DiskTooSmall errors.
The implementation however filled it in from the "current size"
computed by determine_auto_size(), which means something else entirely:
the size of the image as it currently exists, i.e. the GPT metadata
overhead plus the sizes of all existing partitions — a metric designed
for growing image files with --size=auto. For a disk that is being
partitioned from scratch (i.e. carries no partition table yet) that
value is 0.
As a result, when systemd-sysinstall was pointed at a blank 2G disk
that is too small for the OS installation, it reported:
The selected disk is not large enough for an OS installation.
The size of the selected disk is 0B, but a minimal size of 15.7G is required.
Report context->total instead, i.e. the actual size of the block
device, which is also the value the DiskTooSmall check compares the
required size against. Do this in all three places that send
currentSizeBytes, matching the documented semantics of the field.
(cherry picked from commit 588275f426)
Pass relax_extension_release_check through the directory extraction
path instead of hardcoding false. Directory extensions now honor the
same --force relaxation as dissected images.
Reproducer:
cp -a /tmp/app0 /tmp/app10
sudo portablectl attach --force --runtime \
--extension /tmp/app10 /tmp/rootdir app0
Before:
directory image extraction always used strict extension-release name
checks. --force relaxed other extension paths but still rejected a
renamed directory extension with matching metadata.
Follow-up: 06768b90a3
(cherry picked from commit 75d2686794)
Apply SetPoolLimit to IMAGE_PORTABLE instead of IMAGE_MACHINE. The
manager property and the setter now refer to the same portable image
pool.
Reproducer:
sudo portablectl set-limit 1G
busctl get-property org.freedesktop.portable1 \
/org/freedesktop/portable1 \
org.freedesktop.portable1.Manager PoolLimit
Before:
SetPoolLimit adjusted the machine image pool while portablectl
reported the portable image pool. The command could succeed without
changing the limit that portable users queried later.
Follow-up: 824fcb95c9
(cherry picked from commit ddf196a20b)
Normalize the image argument for per-image SetImageLimit requests while
leaving the single-argument pool limit mode unchanged. This aligns quota
handling with other path-taking verbs.
Reproducer:
cd /tmp
portablectl set-limit ./portable-repro 1G
Before:
./portable-repro was sent unchanged to portabled and rejected as an
invalid image name/path. Per-image quota changes worked for absolute
paths but not for equivalent relative paths.
Follow-up: 61d0578b07
(cherry picked from commit f21e820631)
Normalize each remove argument with determine_image() before building
the RemoveImage request. Relative paths are converted before reaching
portabled.
Reproducer:
cd /tmp
portablectl remove ./portable-repro
Before:
./portable-repro was sent unchanged to portabled and rejected as an
invalid image name/path. Removing an image by a relative path failed
even though the path referred to a valid local image.
Follow-up: 61d0578b07
(cherry picked from commit 018c2b229f)
Open unit files with chase_and_openat() rooted at the image instead of
plain openat(). Symlinks are now resolved under the image root and must
end at a regular file.
Reproducer:
ln -s /tmp/portable-host-unit \
IMAGE/usr/lib/systemd/system/PREFIX.service
portablectl inspect --cat IMAGE PREFIX.service
Before:
inspect followed the absolute symlink on the host and printed host
file contents as image unit metadata. Image inspection could leak or
trust files outside the portable image.
Follow-up: 61d0578b07
(cherry picked from commit 797dd11524)
Run read-only image arguments through determine_image() before sending
them over D-Bus. This gives relative paths the same normalized form as
other portablectl image operations.
Reproducer:
cd /tmp
portablectl read-only ./portable-repro true
Before:
./portable-repro was sent unchanged to portabled and rejected as an
invalid image name/path. The same image worked when passed as an
absolute path, so relative paths behaved inconsistently.
Follow-up: 61d0578b07
(cherry picked from commit 57c64f7c69)
Parse the WithExtensions metadata block whenever --force selects that
bus method. This keeps the following unit-file array aligned with the
reply layout.
Reproducer:
portablectl inspect --force /usr/share/minimal_0.raw \
minimal-app0.service
Before:
inspect printed the image metadata but dropped the matching unit
list. Scripts could not see which unit files were selected when
--force was used with the WithExtensions bus reply.
Follow-up: bdfa3f3a5c
(cherry picked from commit 9773189e56)
Lock down the software TPM service: restrict the runtime directory (which
holds the AES key sealing swtpm's state) to 0700, and apply the usual
sandboxing (NoNewPrivileges, MemoryDenyWriteExecute, ProtectSystem-adjacent
Protect*/Restrict* knobs, PrivateNetwork, PrivateTmp, a @system-service
syscall filter, etc.).
A few common knobs can't be used here: the service must keep CAP_SYS_ADMIN
(needed for the ioctl that creates the vtpm proxy device on /dev/vtpmx),
and it needs runtime access to the ESP and its backing block device at a
path only known at runtime, which rules out PrivateDevices=, DevicePolicy=,
ProtectSystem= and User=/DynamicUser=.
Signed-off-by: Paul Meyer <katexochen0@gmail.com>
(cherry picked from commit 2f1c3b8afb)
swtpm keeps its state on the ESP (--tpmstate=dir=) and thus holds it
busy for as long as it runs, but nothing ensured it was stopped before
the ESP was unmounted on shutdown, leaving boot.mount failing to
unmount.
Two things were missing:
- systemd-tpm2-swtpm.service has DefaultDependencies=no, which strips
the implicit shutdown.target membership, so it was torn down late
rather than stopped in an ordered manner. Add
Conflicts=/Before=shutdown.target, as the sibling
systemd-tpm2-setup{,-early}.service units already do.
- The generator only ordered the service
After=boot.automount/efi.automount. Ordering after the .automount
units is enough for start-up, but only an ordering against the actual
.mount units makes the service stop (releasing the ESP) before the
file system is unmounted. Add boot.mount/efi.mount to the After= line;
this is a no-op at start-up, as the mount has no job of its own there
(it is triggered on access via the automount).
Signed-off-by: Paul Meyer <katexochen0@gmail.com>
(cherry picked from commit df2baac2b9)
File names containing backslashes cannot be checked out on Windows,
are not handled properly by build systems such as buck, and are awkward
to work with in general, so store the \x2d slice units under sanitized
file names, and rename them to the real unit name on install via a new
optional "name" key in the units list.
The fuzz-unit-file corpus sample is renamed as well; its file name is
not meaningful to the fuzzer, and dropping the backslash means it is no
longer skipped by the meson workaround for
https://github.com/mesonbuild/meson/issues/1564, so it runs as a
regression test again.
(cherry picked from commit d04fbcf956)
Since 5c6bb28999 image_discover() uses
chaseat() to chase the path to the image. This however breaks the raw
image check in image_make() as "path" is now not the symlink itself, but
the symlink target.
So with:
$ ls -l /var/lib/machines
total 872104
lrwxrwxrwx. 1 root root 12 Jul 14 06:10 foo.raw -> foo.squashfs
-rw-r--r--. 1 root root 5368709120 Jul 13 02:07 foo.squashfs
The endswith(path, ".raw") check is now performed on "/.../foo.squashfs"
instead of "/.../foo.raw", making it false and thus ignoring the image
symlink completely.
Address this by also checking if the pretty name is set - if so, and the
path is a regular file, the caller must've been image_find() or
image_discover() which already checked if the original path ends in .raw
and is a regular file.
Follow-up for 5c6bb28999.
Resolves: #41656
(cherry picked from commit f9348b3bc8)
Previously "machinectl shell --uid=1000 <container>" resulted in a
sucessful drop into a shell in the respective target machine. After
commit a9e9288288 this is no longer the
case.
This fixes the above breaking change brought in commit a9e9288288
(cherry picked from commit 93212e2910)
The function documents that at most 'len' bytes are read from 'str',
but the leading whitespace skip used strspn(), which is bounded only
by a non-whitespace byte or a NUL. ata_id passes the space padded,
non-NUL-terminated ATA IDENTIFY fields, so an all-blank model reads
past the buffer. Use strnspn() to cap the skip to 'len'.
(cherry picked from commit 6b2cb3abeb)
Like strspn(), but reads at most 'n' bytes from the input. strspn()
is bounded only by a non-matching byte or a NUL, so it over-reads a
buffer that is all matching bytes and not NUL terminated within 'n'.
(cherry picked from commit 0af6caff42)