Commit Graph
100 Commits
Author SHA1 Message Date
Frantisek Sumsal efa16c7f31 dns-packet: limit the number of jumps during packet decompression
So far we didn't limit the number of jumps when decompressing a DNS
packet. This can lead to some extra work with specially crafted packets
that send a staircase of thousands of descending compression pointers,
which can then be amplified by referencing such staircase from thousands
of names.

Given that legitimate DNS packets need only a handful of jumps in the
compression chains, let's limit the number of jumps per a single name to
256 (the same limit as, for example, Unbound currently has).

Reported by opensec-intelligence (OpenSec).
2026-09-22 15:55:13 +02:00
Frantisek Sumsal ed0727fecd test: sync the journal before checking it for ordering cycle messages
Otherwise we might get an empty match:

TEST-03-JOBS.sh[100]: + IDS_FILE=/tmp/TEST-03-JOBS-CYCLE-IDS-30241
TEST-03-JOBS.sh[314]: + varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Manager.Describe '{}'
TEST-03-JOBS.sh[315]: + jq .runtime.TransactionsWithOrderingCycle
TEST-03-JOBS.sh[316]: ++ jq length /tmp/TEST-03-JOBS-CYCLE-IDS-30241
TEST-03-JOBS.sh[100]: + [[ 20 -ge 20 ]]
TEST-03-JOBS.sh[100]: + for i in {0..19}
TEST-03-JOBS.sh[317]: ++ jq -r '.[0]' /tmp/TEST-03-JOBS-CYCLE-IDS-30241
TEST-03-JOBS.sh[100]: + journalctl -b TRANSACTION_ID=48 --grep 'cycle starting with'
TEST-03-JOBS.sh[318]: -- No entries --
[FAILED] Failed to start TEST-03-JOBS.service - TEST-03-JOBS.
[  !!  ] Exiting: unit TEST-03-JOBS.service failed

Even though the message was properly emitted:

$ journalctl --file TEST-03-JOBS-1.journal -b TRANSACTION_ID=48 --grep 'cycle starting with'
Aug 11 14:46:15 H systemd[1]: [🡕] transaction-cycle17.service: Unable to break cycle starting with transaction-cycle17.service/start
2026-08-11 16:12:42 +02:00
Frantisek Sumsal 143c284567 test: drop forgotten set +e
This was introduced all the way back in 2019 where this particular test
case was the last one in the file, so it worked as intended.
Unfortunately, since then we added new tests after this one, which means
this particular `set +e` masked fails in any of them (except for
assert_*() calls, which call exit on fail).
2026-08-11 16:12:42 +02:00
Frantisek Sumsal 2c4e86c290 test: fix & enhance test for RestartMode=
Stay `set +e` in the test masked that this test has apparently never
worked in the first place: when a unit is killed with a signal, any
bound unit is stopped retroactively via
retroactively_stop_dependencies() but it's not restarted again, as the
restart for the bound unit is queued as TRY_RESTART in a following
transaction - but at this point the bound unit is already inactive, so
the TRY_RESTART job collapses into a NOP.

Let's account for this and also add a couple of test cases for
RestartMode= with an explicit restart, which should trigger the bound
unit as well (assuming it's active).

Follow-up for 9b42c58a2b.
2026-08-11 16:11:25 +02:00
Frantisek Sumsal 2b81535f74 test: fix typo in test unit
Follow-up for bf3dfa6202.
2026-08-11 12:40:53 +02:00
Frantisek Sumsal ae940730c6 sd-journal: prefer machine ID from the tail entry
compare_boot_ids() (from 262299dccb) skips
the tail entry realtime timestamp ordering when the compared locations
are from two different machines (and hence have two different machine
IDs) - in that case we fall through to a realtime comparison between the
two locations. This works fine except for journals that were imported
via systemd-journal-remote - the machine ID in the journal header is set
to the receiving machine's ID instead of the original one; but the
original machine ID is kept in the _MACHINE_ID= field.

This, however, defeats the check in compare_boot_ids() so we treat the
remote journals as local ones and try to compare the boots based on
their tail entry realtime timestamps, which results in grouping the
entries by their boot ID instead of interleaving them by their realtime
timestamp.

Given that we (transitively) call journal_file_read_tail_timestamp()
from compare_boot_ids(), where we both (re)set the newest_machine_id
field to the machine ID from the journal header and have the tail entry
at hand, let's try to get the _MACHINE_ID= field from the tail entry and
set newest_machine_id to that if it's present and valid. This way
compare_boot_ids() will correctly determine that the entries are from
two different machines and compare_locations() eventually falls through
to the entry realtime check, so we end up with properly interleaved
entries instead of entries grouped by boot ID.

The performance impact should be pretty negligible in vast majority of
cases, as we'd do this only when:
  - opening a new file
  - comparing entries across different boots
  - advancing to a next location

The first case is a one-time thing only per journal. The second and
third case might call this a bit more frequently, but we fall through to
the _MACHINE_ID= parsing only if the journal file got a new entry since
the last check. The most significant impact would be on journals that
are being actively written into, but even then iterating through the
handful of typical fields is negligible compared to what the rest of the
function does. Also, since 8f8ab4bf98 the
journal_file_read_tail_timestamp() calls are ratelimited when walking
through the journal, lessening the impact even further.

Resolves: #34169
2026-08-08 04:05:25 +09:00
Frantisek Sumsal 2ade235150 Revert "ci: fix /etc and /usr ownership"
The issue with the GHA images should be resolved.

This reverts commit 6d7a2ec6ba.
2026-08-06 05:42:54 +09:00
Frantisek Sumsal 8e762e6b9f test: verify cursor-seeking in the opposite direction as well 2026-08-05 21:06:43 +02:00
Frantisek Sumsal 868d07d1d4 sd-journal: prefer exact cursor match after seek
Since 262299dccb compare_locations() falls
through to a realtime-based comparison in compare_boot_ids() if the
compared entries have different boot IDs (and seqnum IDs). This can,
however, cause us to pick up the wrong entry/file on systems with
unreliable/missing RTC, where early-boot entries of a boot might have
earlier realtime timestamp compared to the previous boot.

For example, here's a minimal reproducer consisting of two journal files
containing three entries from two separate boot IDs:

$ journalctl --file one.journal -o export
__CURSOR=s=ba37c62fcacf406aad088166c4963cd1;i=1;b=c220efc91dd440deb89d82c98ddb11a5;m=186a0;t=1e8480;x=39d4323a8e5a248d
__REALTIME_TIMESTAMP=2000000
__MONOTONIC_TIMESTAMP=100000
__SEQNUM=1
__SEQNUM_ID=ba37c62fcacf406aad088166c4963cd1
_BOOT_ID=c220efc91dd440deb89d82c98ddb11a5
NUMBER=1

$ journalctl --file two.journal -o export
__CURSOR=s=9d15197efcc54d8ca5d6e7cce28373e2;i=1;b=686dc35e78f64b6ba126018ab6e8bae9;m=186a0;t=f4240;x=57db1c7bd38b37ed
__REALTIME_TIMESTAMP=1000000
__MONOTONIC_TIMESTAMP=100000
__SEQNUM=1
__SEQNUM_ID=9d15197efcc54d8ca5d6e7cce28373e2
_BOOT_ID=686dc35e78f64b6ba126018ab6e8bae9
NUMBER=2

__CURSOR=s=9d15197efcc54d8ca5d6e7cce28373e2;i=2;b=686dc35e78f64b6ba126018ab6e8bae9;m=30d40;t=2dc6c0;x=3513732159948113
__REALTIME_TIMESTAMP=3000000
__MONOTONIC_TIMESTAMP=200000
__SEQNUM=2
__SEQNUM_ID=9d15197efcc54d8ca5d6e7cce28373e2
_BOOT_ID=686dc35e78f64b6ba126018ab6e8bae9
NUMBER=3

The first file (one.journal) contains a single entry from the first boot
(c220efc91dd440deb89d82c98ddb11a5) and a realtime timestamp of 2s. The
second file (two.journal) contains two entries from the second boot
(686dc35e78f64b6ba126018ab6e8bae9) - the first entry has a realtime
timestamp of 1s (i.e. _earlier_ than the tail timestamp of the first
boot), and the second entry has a realtime timestamp of 3s (i.e.  after
a time correction).

This works fine for forward iteration, but seeking directly to the
second entry is when things go south:

First entry:
$ python3 <<EOF
c = "s=ba37c62fcacf406aad088166c4963cd1;i=1;b=c220efc91dd440deb89d82c98ddb11a5;m=186a0;t=1e8480;x=39d4323a8e5a248d"
from systemd import journal; j = journal.Reader(path="."); j.seek_cursor(c); j.get_next(); print(j.test_cursor(c))
EOF
True

Second entry:
$ python3 <<EOF
c = "s=9d15197efcc54d8ca5d6e7cce28373e2;i=1;b=686dc35e78f64b6ba126018ab6e8bae9;m=186a0;t=f4240;x=57db1c7bd38b37ed"
from systemd import journal; j = journal.Reader(path="."); j.seek_cursor(c); j.get_next(); print(j.test_cursor(c))
EOF
False

That's because:
  - we call sd_journal_seek_cursor(...) and parse it (seqnum, seqnum_id,
    boot_id, realtime, ...)
  - we call sd_journal_next() -> real_journal_next() which iterates over
    all open journal files to find all candidate entries and pick the
    best one
  - for each journal file we call next_beyond_location()
  - for next_beyond_location(one.journal) we get:
    - last_direction is _DIRECTION_INVALID, so we call find_location_with_matches()
    - we fall all the way through to journal_file_move_to_entry_by_realtime()
    - here we try to find the first entry with realtime >= 1s; this file
      has only one entry with realtime = 2s which matches
  - we got a match and new_file is unset, so we set it to one.journal
  - for next_beyond_location(two.journal) we get:
    - we fall through to find_location_with_matches()
    - here we have a matching seqnum, so we call journal_file_move_to_entry_by_seqnum()
  - we got another match but new_file is set, so we call compare_locations(two.journal, one.journal):
    - we fall through to compare_boot_ids()
    - we get the tail timestamps of both journal files and return
      CMP(second boot, first boot) -> CMP(3s, 2s) = 1
  - this bubbles up back to real_journal_next() where found = false, so
    new_file remains one.journal (the wrong journal file)
  - this is followed by set_location(one.journal), which makes the
    following sd_journal_test_cursor() call fail, as the
    boot_id/seqnum_id/... of the current position don't match with the
    cursor

To fix this, let's track the journal file that holds the exact cursor
match, and override the chosen candidate if needed once we process all
open journals.

Resolves: #31516
2026-08-05 21:06:43 +02:00
Frantisek Sumsal 36a9875c1d sysusers: gracefully handle existing group in /etc/gshadow
systemd-sysusers currently skips entries for groups that already exists.
However, if a group exists only in /etc/gshadow and not in /etc/group,
systemd-sysusers panics:

~# groupdel sgx
~# systemd-sysusers; echo $?
Creating group 'sgx' with GID 106.
0
~# systemd-sysusers; echo $?
0
~# sed -i '/sgx/d' /etc/group
~# systemd-sysusers; echo $?
Creating group 'sgx' with GID 106.
/etc/gshadow: Group "sgx" already exists.
1

To fix this, let's mirror the logic from the user path
(write_temporary_shadow()) where we simply drop the user from the "todo"
queue if it already exists in /etc/shadow instead of throwing an error.
2026-08-04 08:17:04 +09:00
Frantisek Sumsal 56d2162bff po: resynchronize translations on Weblate
Weblate got itself into a conflict and while resolving it it forced a
resynchronization of all translations, which in combination with a new
version of Weblate triggered a lot of rather pointless
multiline-to-singleline (and vice versa) changes. Let's squash all this
noise into a single commit to make both Weblate and us happy.

C.f. https://github.com/systemd/systemd/pull/43248.
2026-08-02 16:54:09 +09:00
Frantisek Sumsal 6d7a2ec6ba ci: fix /etc and /usr ownership
Recent Ubuntu 24.04 GHA images have /etc and /usr owned by runner
instead of root, which breaks some of our tests. This has been filed to
GH as https://github.com/actions/runner-images/issues/14477, so let's
work around this in our jobs until it's fixed.
2026-07-30 15:25:30 +01:00
Frantisek Sumsal 9d4019b9b7 ci: build the release clang build with _FORTIFY_SOURCE=3
Since this combination is known to cause interesting issues in our
allocation machinery.
2026-07-30 01:18:58 +09:00
Frantisek Sumsal a56d7db48f alloc-util: make malloc_sizeof_safe() compatible with clang's _FORTIFY_SOURCE=3
Turns out that clang's interprocedural analysis is quite smart and can
look through our expand_to_usable() trick, which then causes crashes
with _FORTIFY_SOURCE=3.

clang's interprocedural analysis can see that expand_to_usable() simply
returns its first argument, so it replaces all uses of the return value
with that argument (the original realloc() result). This effectively
bypasses expand_to_usable()'s alloc_size attribute, causing the fortify
check to use the (smaller) size from realloc() instead, which eventually
leads to a false-positive buffer overflow:

$ build/test-varlink-idl
/* test_parse_format */
...
*** buffer overflow detected ***: terminated
Aborted                    (core dumped) build/test-varlink-idl

This is not an issue with gcc (at least not yet), since gcc sees
expand_to_usable() as an opaque user-defined allocation-like function
and simply trusts the alloc_size attribute that comes with it.

To fix this, let's add a simple no-op barrier to malloc_sizeof_safe()
that clobbers the input pointer, which causes
__builtin_dynamic_object_size() to return (size_t)-1 - this is
interpreted as an "unknown" size by the following fortify check which is
then skipped instead of triggering the assertion.

Similarly, test-alloc-util now doesn't call malloc_usable_size()
directly but instead goes through malloc_sizeof_safe(), so it's also
guarded by the barrier.

This follows the already established pile of similar workaround for the
same class of issues we encountered with gcc, namely [0], which prompted
[1], that was later reverted in [2], and then followed by another couple
of fixes in [3] and [4].

Resolves: #43178

[0] https://github.com/systemd/systemd/issues/22801
[1] https://github.com/systemd/systemd/commit/0bd292567a543d124cd303f7dd61169a209cae64
[2] https://github.com/systemd/systemd/commit/2cfb790391958ada34284290af1f9ab863a515c7
[3] https://github.com/systemd/systemd/commit/7929e180aa47a2692ad4f053afac2857d7198758
[4] https://github.com/systemd/systemd/commit/4f79f545b3c46c358666c9f5f2b384fe50aac4b4
2026-07-30 01:18:58 +09:00
Frantisek Sumsal f978a4a1e0 Revert "test: re-enable sync in TEST-25-IMPORT"
This shouldn't be necessary anymore, as we don't block openat2() when
running nspawn with --suppress-sync=yes.

This reverts commit c955e24916.
2026-07-24 14:10:58 +09:00
Frantisek Sumsal fb73687370 seccomp-util: allow openat2() with --suppress-sync=yes
When --suppress-sync=yes was introduced in
4a4654e024 it filtered out openat2()
completely, as we can't check its "flags" argument because it's hidden
in an indirect struct. This was perfectly fine at that time, as
openat2() was quite new and software shipped with a fallback to
open()/openat() if the syscall wasn't present.

However, today the situation is different and an increasing number of
software is moving to openat2() without any fallback - tar [0] being the
most common one in the recent reports and workarounds, and attr recently
fixed a CVE by switching to openat2() [1] as well, to name a few.

Given that we already block all the sync-family syscalls and calling
openat2() with O_SYNC is relatively rare, let's just blanket-enable it
in the --suppress-sync=yes mode. This means that we might issue a
synchronous write when something calls openat2() with O_SYNC, but not
breaking the apps here feels like a reasonable trade-off, at least until
a better solution pops up.

Note that the same situation is in seccomp_restrict_sxid(), but allowing
the openat2() syscall there could actually have some security-related
implications under certain circumstances.

Resolves: #41868

[0] https://cgit.git.savannah.gnu.org/cgit/tar.git/commit/?id=75b03fdff48916bd0654677ed21379bdb0db016d
[1] https://cgit.git.savannah.gnu.org/cgit/attr.git/commit/?id=1cea2fc5e8b5c8a5f29abf1141399e01a57a3154
2026-07-24 14:10:58 +09:00
Frantisek Sumsal f9348b3bc8 discover-image: don't ignore symlinks to raw images
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
2026-07-16 14:31:32 +02:00
Frantisek Sumsal 5f2d15abf4 timer: avoid re-arming WakeSystem=yes timer after suspend
There's an edge case where a single-shot timer using WakeSystem=yes
could get re-armed after elapsing. For example, when a timer is created
using:

$ systemd-run --user --on-active="1m" --timer-property=WakeSystem=yes flatpak run io.bassi.Amberol

and the system is then suspended, following sequence of events may
happen:

Jul 08 06:57:25 systemd[2640]: run-p192456-i199160.timer: Installed new job run-p192456-i199160.timer/start as 7675
Jul 08 06:57:25 systemd[2640]: run-p192456-i199160.timer: Enqueued job run-p192456-i199160.timer/start as 7675
Jul 08 06:57:25 systemd[2640]: run-p192456-i199160.timer: Monotonic timer elapses in 59.999999s.
Jul 08 06:57:25 systemd[2640]: run-p192456-i199160.timer: Changed dead -> waiting
Jul 08 06:57:25 systemd[2640]: run-p192456-i199160.timer: Job 7675 run-p192456-i199160.timer/start finished, result=done
Jul 08 06:57:25 systemd[2640]: Started [systemd-run] /usr/bin/flatpak run io.bassi.Amberol.
Jul 08 06:58:13 systemd[2640]: run-p192456-i199160.timer: Time change, recalculating next elapse.
Jul 08 06:58:13 systemd[2640]: run-p192456-i199160.timer: Monotonic timer elapses in 12.785674s.
Jul 08 06:58:26 systemd[2640]: run-p192456-i199160.timer: Timer elapsed.
Jul 08 06:58:26 systemd[2640]: run-p192456-i199160.timer: Changed waiting -> running
Jul 08 06:58:29 systemd[2640]: run-p192456-i199160.timer: Got notified about unit deactivation.
Jul 08 06:58:29 systemd[2640]: run-p192456-i199160.timer: Monotonic timer elapses in 33.544681s.
Jul 08 06:58:29 systemd[2640]: run-p192456-i199160.timer: Changed running -> waiting
Jul 08 06:59:48 systemd[2640]: run-p192456-i199160.timer: Timer elapsed.

1) The timer is armed at 06:57:25. timer_enter_waiting()
   calculates following values:

   base = M0 (current CLOCK_MONOTONIC)
   usec_shift_clock(base, CLOCK_MONOTONIC, CLOCK_BOOTTIME_ALARM) = M0 (no delta yet)
   v->next_elapse = M0 + 60s

   The timer is on CLOCK_BOOTTIME_ALARM at M0 + 60s

2) System suspends. If the system ran for ~12s and was suspended for
   ~36s, at resume we'd get:

   CLOCK_MONOTONIC = M0 + 12s
   CLOCK_BOOTTIME = M0 + 12s + 36s = M0 + 48s

3) timer_time_change() fires and calls timer_enter_waiting(t, true)

   Because time_change=true, v->next_elapse is not recalculated and
   keeps the original M0 + 60s value. The timer then correctly computes
   the remaining time as ~12.8 seconds:

        Jul 08 06:58:13 ...: Monotonic timer elapses in 12.785674s.

4) Timer elapses at 06:58:26

        Jul 08 06:58:26 ...: Timer elapsed.

   timer_enter_running() sets last_trigger.monotonic to CLOCK_MONOTONIC
   (which equals to M0 + 12s before suspend + 13s after suspend, thus
   + 25s)

5) Unit deactivates at 06:58:29

   timer_trigger_notify() calls timer_enter_waiting(t, false) -
   time_change=false; that means that this time v->next_elapse gets
   recalculated:

   base = inactive_exit_timestamp.monotonic = M0 (i.e. when the timer was originally armed)
   usec_shift_clock(M0, CLOCK_MONOTONIC, CLOCK_BOOTTIME_ALARM)
        a = now(CLOCK_MONOTONIC) = M0 + 28s
        b = now(CLOCK_BOOTTIME_ALARM) = M0 + 64s
        result = b - (a - M0) = (M0 + 64s) - 28s = M0 + 36s => the time spent in suspend

   Hence v->next_elapse = (M0 + 36s) + 60s = M0 + 96s

   This then skips disabling of one-shot timers in the following check,
   because the expression

        v->next_elapse < triple_timestamp_by_clock(&ts, TIMER_MONOTONIC_CLOCK(t))

   is false, because the v->next_elapse (M0 + 96s) is not less than the
   current time (M0 + 64s), so the timer is re-armed again:

        Jul 08 06:58:29 ...: Monotonic timer elapses in 33.544681s

Let's mitigate this by skipping the next elapse timestamp recalculations
for one-shot timers for which we've already calculated the value in this
activation cycle.

Resolves: #42929
2026-07-13 16:23:59 +01:00
Frantisek Sumsal 9ae50c78cc sd-event: use CLOCK_BOOTTIME for rate limits
When a rate limit is armed, for example via StartLimitInterval=, and the
machine is suspended, the rate limit's "clock" is suspended as well,
since the elapse checks use CLOCK_MONOTONIC. This then causes unexpected
situations where a rate limit armed via StartLimitInterval=1h, followed
by a 10 hour suspend, would elapse after 11 hours total.

Let's avoid this by switching the rate limits to CLOCK_BOOTTIME, which
works the same as CLOCK_MONOTONIC, but accounts for the time spent in
suspend as well.

There's one slight concern when it comes to upgrade path - the old
"begin" value of the rate limit is stored as CLOCK_MONOTONIC, but after
upgrading systemd and serializing/deserializing the state it will be
suddenly compared against now(CLOCK_BOOTTIME), which might cause some
rate limits to elapse "prematurely". But this is just a one-time thing,
after which the rate limit timers should re-assess themselves.

Resolves: #42912
2026-07-08 15:38:39 +01:00
Frantisek Sumsal 7273d38335 test: ignore fails when the formatted timezone differs from the current one
When formatting a timestamp the C API takes into account historical data
from tzdata, so it returns a date strings with a historically-correct
timezone abbreviation. However, tzname[] doesn't do this and it returns
the most recent abbreviation for the given zone.

For example, according to tzdata America/Cancun switched from EST/EDT to
CST/CDT on 1998-08-02:

Zone America/Cancun     -5:47:04 -      LMT     1922 Jan  1  6:00u
                        -6:00   -       CST     1981 Dec 26  2:00
                        -5:00   -       EST     1983 Jan  4  0:00
                        -6:00   Mexico  C%sT    1997 Oct 26  2:00
                        -5:00   Mexico  E%sT    1998 Aug  2  2:00
                        -6:00   Mexico  C%sT    2015 Feb  1  2:00
                        -5:00   -       EST

So, formatting a timestamp from this time will yield a string with the
EDT timezone:

$ TZ=America/Cancun date -d "@902035565"
Sun Aug  2 01:26:05 EDT 1998

But using tzname[] (or strptime %z) shows the most recent data, where
America/Cancun uses EST (and doesn't use DST anymore, hence
tzname[1]=CDT that glibc remembers from the previous zone epoch):

$ TZ=America/Cancun ./tz
{EST, CDT}

This means that when we parse the formatted timestamp back we don't use
the historical timezone data, so we might end up with a different
offset:

TZ=America/Cancun, tzname[0]=EST, tzname[1]=CDT
@902035565603993 → Sun 1998-08-02 01:26:05 EDT → @902039165000000 → Sun 1998-08-02 01:26:05 CDT
src/test/test-time-util.c:452: Assertion failed: Expected "ignore" to be true
Aborted                    (core dumped) build-local/test-time-util

Instead of adding exceptions for every single timezone that switched
between different offsets in the past, let's address this a bit more
generally and skip the check if the parsed timezone doesn't match any of
the current timezones - this still keeps the check that the time
difference in such case is exactly one hour, so its effect should be
limited mostly to DST-related changes.

Resolves: #37684
2026-07-03 07:23:30 +09:00
Frantisek Sumsal 60d982d3fc test: suppress fails on the Africa/Tripoli (Libya) timezone
As it switched between CET and EET multiple times in the past, causing
random fails with certain historical timestamps:

834/1524 test - systemd:test-time-util     FAIL    0.96s   killed by signal 6 SIGABRT
 ...
TZ=Africa/Tripoli, tzname[0]=EET, tzname[1]=CEST
@378687574661411 → Thu 1981-12-31 23:59:34 CET → @378683974000000 → Thu 1981-12-31 23:59:34 EET
src/test/test-time-util.c:450: Assertion failed: Expected "ignore" to be true
/builddir/build/BUILD/systemd-261.1-build/systemd-261.1/tools/test-crash-trace.sh: line 18: 87088 Aborted                    (core dumped) "$@"
2026-07-02 13:17:06 +01:00
Frantisek Sumsal 7804fd182c vmspawn: complain loudly if we can't prepare a unix socket for virtiofsd
I couldn't convince vmspawn to start a VM on a Fedora image I just
downloaded, and it was pretty light on any useful details:

$ build/systemd-vmspawn --image ~/Downloads/Fedora-Server-Guest-Generic-Rawhide-20260627.n.0.x86_64.qcow2 --image-format=qcow2 --bind-ro=/tmp/bar; echo $?
░ Spawning VM Fedora-Server-Guest-Generic-Rawhide-20260627.n.0.x8664.qcow2 on /home/mrc0mmand/Downloads/Fedora-Server-Guest-Generic-Rawhide-20260627.n.0.x86_64.qcow2.
░ Press Ctrl-] three times within 1s to kill VM.
1

Turns out that the unix socket path vmspawn generates for the virtiofsd
socket is too long. Let's relay this information to the user as well to
make debugging this a little less painful:

$ build/systemd-vmspawn --image ~/Downloads/Fedora-Server-Guest-Generic-Rawhide-20260627.n.0.x86_64.qcow2 --image-format=qcow2 --bind-ro=/tmp/bar
░ Spawning VM Fedora-Server-Guest-Generic-Rawhide-20260627.n.0.x8664.qcow2 on /home/mrc0mmand/Downloads/Fedora-Server-Guest-Generic-Rawhide-20260627.n.0.x86_64.qcow2.
░ Press Ctrl-] three times within 1s to kill VM.
Failed to prepare unix socket '/run/user/1000/systemd/vmspawn/Fedora-Server-Guest-Generic-Rawhide-20260627.n.0.x8664.qcow2/sock-9594581dcf598992': File name too long
2026-06-28 12:52:02 +09:00
Frantisek Sumsal 14b1a92eb2 unit-name: introduce "strict" mode for unit name mangling
unit_name_mangle_with_suffix() is quite benevolent by default and allows
the unit to "transition" into a different unit type than what's
requested via its suffix argument. For example, calling
unit_name_mangle_with_suffix() with "/foo/bar" as a unit name and
".service" as a suffix would give you "foo-bar.mount", without any
warning or error.

This could then lead to a quite confusing errors in certain situations:

~# systemd-run --remain-after-exit --unit /foo/bar true
Failed to start transient service unit: Cannot set property RemainAfterExit, or unknown property.

Given we can't change the default behaviour of
unit_name_mangle_with_suffix() as some parts of systemd already depend
on its "benevolence" (like systemctl), let's introduce a new flag -
UNIT_NAME_MANGLE_STRICT - that checks if the mangled/resolved unit
name's suffix matches the requested one and errors out if not.

With the flag used throughout systemd-run's code, the error in the above
case is now a bit more clear:

~# build/systemd-run --remain-after-exit --unit /foo/bar true
Path "/foo/bar" resolves to unit type "mount", but "service" is expected as unit.
Failed to mangle unit name: Invalid argument

Resolves: #39996
2026-06-25 12:58:06 +02:00
Frantisek Sumsal 89f1f76624 unit-name: use FLAGS_SET() more 2026-06-25 11:31:28 +02:00
Frantisek Sumsal 926bf65359 test: skip fdstore tests if test-fdstore is not available
When the test suite is run in the "standalone" mode, the minimal
container might not contain the test-fdstore binary that's needed for a
couple of tests. Since installing systemd-tests into the minimal
container pulls in a lot of other dependencies, let's just skip the
affected tests instead to avoid this.
2026-06-23 23:04:47 +01:00
Frantisek Sumsal d1f7bd900c po: Translated using Weblate (Serbian)
Currently translated at 100.0% (285 of 285 strings)

Co-authored-by: Frantisek Sumsal <frantisek@sumsal.cz>
Translate-URL: https://translate.fedoraproject.org/projects/systemd/main/sr/
Translation: systemd/main
2026-06-08 03:52:59 +00:00
Frantisek Sumsal e606827bfd po: Translated using Weblate (Slovak)
Currently translated at 16.8% (48 of 285 strings)

Co-authored-by: Frantisek Sumsal <frantisek@sumsal.cz>
Translate-URL: https://translate.fedoraproject.org/projects/systemd/main/sk/
Translation: systemd/main
2026-06-08 03:52:59 +00:00
Frantisek Sumsal 70429d67d5 test: read the namespace symlink directly instead of using lsns
lsns scans all of /proc/*/ns/* even when a single PID is specified which
can be racy. Let's just read and compare the procfs symlinks directly to
both avoid doing the whole procfs scan and to, hopefully, get rid of the
occasional test fail where lsns fails to resolve the namespace for the
"old" process:

[ 1517.791471] TEST-07-PID1.sh[22279]: + systemd-run --unit=oldservice --property=Type=notify --property=NotifyAccess=all --property=PrivateUsers=true --property=PrivateNetwork=true bash -c 'systemd-notify --ready; exec sleep>
[ 1517.839514] TEST-07-PID1.sh[22329]: Running as unit: oldservice.service; invocation ID: 840ba49490f349c8b3b2116a43fb6f54
[ 1517.840403] TEST-07-PID1.sh[22335]: ++ systemctl show oldservice -p MainPID
[ 1517.841707] TEST-07-PID1.sh[22336]: ++ awk -F= '{print $2}'
[ 1517.849691] TEST-07-PID1.sh[22279]: + OLD_PID=22330
[ 1517.849691] TEST-07-PID1.sh[22279]: + systemd-run --unit=newservice --property=Type=notify --property=NotifyAccess=all --property=UserNamespacePath=/proc/22330/ns/user --property=NetworkNamespacePath=/proc/22330/ns/net bas>
[ 1517.951239] TEST-07-PID1.sh[22337]: Running as unit: newservice.service; invocation ID: 515b7f71240344aba2864e8510a18cbf
[ 1517.951475] TEST-07-PID1.sh[22343]: ++ awk -F= '{print $2}'
[ 1517.951915] TEST-07-PID1.sh[22342]: ++ systemctl show newservice -p MainPID
[ 1517.972557] TEST-07-PID1.sh[22279]: + NEW_PID=22338
[ 1517.972696] TEST-07-PID1.sh[22344]: ++ lsns -p 22330 -o NS -t net -n
[ 1518.003546] TEST-07-PID1.sh[22345]: ++ lsns -p 22338 -o NS -t net -n
[ 1518.030831] TEST-07-PID1.sh[22279]: + assert_eq '' 4026532280
[ 1518.031106] TEST-07-PID1.sh[22348]: + set +ex
[ 1518.031106] TEST-07-PID1.sh[22348]: FAIL: expected: '4026532280' actual: ''
[ 1518.031342] TEST-07-PID1.sh[111]: + echo 'Subtest /usr/lib/systemd/tests/testdata/units/TEST-07-PID1.user-namespace-path.sh failed'

Also, don't swallow the exit code from the command substitution in case
readlink fails, which could lead to a false-positive test result if it
happened for both readlinks.

Resolves: #39546
2026-06-05 04:39:00 +09:00
Frantisek Sumsal 223ca6d2d3 import: don't assert on missing signature file
Since ac9edf9911 even the per-file
verification style uses detached signatures instead of inline ones.
That commit dropped the inline verification fallback, but it didn't
update the raw_pull_job_on_finished()/tar_pull_job_on_finished()
callbacks, which caused the per-file verification to silently fall
through the signature check and eventually hit the now unconditional
assert in pull_verify():

~# importctl pull-tar -mN https://distfiles.gentoo.org/releases/amd64/autobuilds/20260426T153103Z/stage3-amd64-systemd-20260426T153103Z.tar.xz
...
Acquired 1.2G for https://distfiles.gentoo.org/releases/amd64/autobuilds/20260426T153103Z/stage3-amd64-systemd-20260426T153103Z.tar.xz.
Download of https://distfiles.gentoo.org/releases/amd64/autobuilds/20260426T153103Z/stage3-amd64-systemd-20260426T153103Z.tar.xz complete.
SHA256 checksum of https://distfiles.gentoo.org/releases/amd64/autobuilds/20260426T153103Z/stage3-amd64-systemd-20260426T153103Z.tar.xz is valid.
Assertion 'signature_job->state == PULL_JOB_DONE' failed at src/import/pull-common.c:588, function pull_verify(). Aborting.

To fix this, let's drop the now outdated guards from the callbacks,
given that both per-file and per-directory verification uses detached
signatures. Also, drop some dead code along the way, given we no longer
need to even determine the verification style anymore.

With this, the reproducer above now shows a proper error:

...
Acquired 1.2G for https://distfiles.gentoo.org/releases/amd64/autobuilds/20260426T153103Z/stage3-amd64-systemd-20260426T153103Z.tar.xz.
Download of https://distfiles.gentoo.org/releases/amd64/autobuilds/20260426T153103Z/stage3-amd64-systemd-20260426T153103Z.tar.xz complete.
Failed to retrieve signature file, cannot verify. (Try --verify=no?)

Also, both --verify=checksum/--verify=no keep working as expected.

Follow-up for ac9edf9911.
Resolves: #41895
2026-05-28 01:24:54 +09:00
Frantisek Sumsal 63ff5dc1e5 Translations update from Fedora Weblate (#42334)
Translations update from [Fedora
Weblate](https://translate.fedoraproject.org) for
[systemd/main](https://translate.fedoraproject.org/projects/systemd/main/).



Current translation status:

![Weblate translation
status](https://translate.fedoraproject.org/widget/systemd/main/horizontal-auto.svg)
2026-05-27 16:07:23 +02:00
Frantisek Sumsal 2e117e270d nspawn: mark --bind-user-shell=/BindUserShell= as trusted
Similarly to --bind-user=/BindUser=.

Follow-up for a9e860f22e.

Reported-by: omkhar <omkhar@users.noreply.github.com>
2026-05-26 17:44:02 +02:00
Frantisek Sumsal 180985c2ce dns-packet: bail out early if the packet is too short
Let's bail out early if the packet claims to contain some
questions or answer RRs, but the remaining packet data size is not
enough to hold a single such entry.

Follow-up for e7cd836dcf.
2026-05-20 11:16:29 +02:00
Frantisek Sumsal 6c119e11b2 dns-packet: drop unnecessary indentation
Bail out early if QDCOUNT == 0, similarly to what we already do in
dns_packet_extract_answer() when RRCOUNT == 0.
2026-05-20 11:16:29 +02:00
Frantisek Sumsal e7cd836dcf resolve: cap pre-allocation for questions/RRs
Since [0] and [1] questions & answer RRs from the incoming packets are
parsed into a hashmap to speed things up. The hashmaps are even
pre-allocated to speed things up even more, but there's one caveat - the
size for the pre-allocation comes from one or more fields from the
incoming packets that are under sender's control.

This can be abused by a malicious DNS server which can send a packet
with a spoofed QDCOUNT (for question packets) or ANCOUNT/NSCOUNT/ARCOUNT
(for answer packets). The limit of the final value in both cases is 64K.
This value is then used to pre-allocate the hashmap (via
set_reserve()/ordered_set_reserve(), where the caller also multiplies
the input value by 2 in both cases), which in turns calls
resize_buckets() that memzero()s the pre-allocated area, so all the
pages are faulted in, showing in process' RSS. Each such spoofed packet
then can translate into a ~4 MiB allocation in the systemd-resolved
process, which doesn't sound that bad.

However, this can be further amplified if the spoofed packet ends up in
resolved's cache. So, if the spoofed packet contains one valid A record
and then an OPT record with a spoofed ARCOUNT, the whole packet ends up
in the cache that can hold 4K of entries, which can eventually cause
resolved to keep up to 16 GiB of memory just for the cache (and thanks
to the memzero() above it's all RSS). Note that all this requires
someone with enough privileges to configure resolved to actually point
to such malicious DNS server or it could come from a malicious DHCP
server on the network. This could also get exploited via LLMNR, but in
thas case an attacker would have to match an ID of a valid transaction
for the packet to end up in resolved's cache.

For example, with a malicious DNS already in resolved configuration:

$ resolvectl dns eth0
Link 2 (eth0): 192.168.99.1:5354

Filling resolved's cache:

$ for i in {0..4200}; do resolvectl query test-$i.example.com; done
...
test-4200.example.com: 192.0.2.1               -- link: dummy0

-- Information acquired via protocol DNS in 1.6ms.
-- Data is authenticated: no; Data was acquired via local or encrypted transport: no
-- Data from: network

Yields following memory increase:

$ while :; do grep VmRSS /proc/$(pidof systemd-resolved)/status; sleep 1; done
VmRSS:     14280 kB
VmRSS:     14280 kB
...
VmRSS:    403352 kB
VmRSS:   1017976 kB
VmRSS:   1603876 kB
VmRSS:   2202028 kB
...
VmRSS:  16795724 kB
VmRSS:  16795724 kB

In my testing I also noticed one annoyance - after certain threshold the
RSS increase persisted even after the malicious entries were evicted
from the cache (or flushed via `resolvectl flush-caches`). This was most
likely due to mmap_threshold getting bumped to > 4 MiB and neither cache
eviction nor flush-caches call malloc_trim(0) (via
sd_event_trim_memory() or similar).

To mitigate this, let's cap the pre-allocation to a maximum number of
records the given packet body can realistically contain. If the minimum
size would be, for whatever unlikely reason, not enough, nothing serious
would happen - the hashmap would still get resized automatically by
resize_buckets(), it'd be just slightly slower.

[0] ae45e1a383
[1] 2d34cf0c16
2026-05-20 03:13:38 +09:00
Frantisek Sumsal 7d1b6b7b96 profile: bail out early if promptvars is disabled
We need promptvars, otherwise the prompt strings won't undergo parameter
expansion and we'd print them literally:

$ shopt -u promptvars
$ echo foo
$(__systemd_osc_context_ps0)foo

Resolves: #40620
2026-05-14 23:17:40 +09:00
Frantisek Sumsal 7cecff8c99 sd-bus: handle non-string keys in dictionaries in JSON dump
JSON only supports string keys in objects, but D-Bus specification is a
bit more lenient and allows dict entries to have any basic type as key.
Let's stringify allowed non-string keys so that we can represent them as
JSON objects.

Relevant snippet from the D-Bus specification:

  A DICT_ENTRY works exactly like a struct, but rather than parentheses
  it uses curly braces, and it has more restrictions. The restrictions
  are: it occurs only as an array element type; it has exactly two
  single complete types inside the curly braces; the first single
  complete type (the "key") must be a basic type rather than a container
  type. Implementations must not accept dict entries outside of arrays,
  must not accept dict entries with zero, one, or more than two fields,
  and must not accept dict entries with non-basic-typed keys. A dict
  entry is always a key-value pair.

Resolves: #32904
2026-05-13 07:50:32 +09:00
Frantisek Sumsal 480449db85 machine: require normalized paths in Copy* D-Bus/Varlink methods
Also, do the same for the Varlink BindMount method, since its D-Bus
counterpart already does it.
2026-05-08 17:20:37 +02:00
Frantisek Sumsal a7e276a82f dbus: limit the number of env variables to something reasonable, vol. 3
Let's limit the number of environment variables when creating a
transient unit via StartTransientUnit as well, since validating the
environment variable names/assignments is expensive.

Follow-up for 49c1e1bcf2.
2026-05-06 16:34:32 +01:00
Frantisek Sumsal 17e6a3e2a8 resolve: limit the number NTAs to something sensible 2026-05-04 22:39:05 +02:00
Frantisek Sumsal 50138e36ff resolve: enforce the search domain limit earlier
The search domain limit is already enforced by dns_search_domain_new(),
but in this case it's way too late. Let's enforce it during the first
loop to avoid unnecessary parsing.
2026-05-04 21:38:56 +02:00
Frantisek Sumsal 49c1e1bcf2 dbus: limit the number of env variables to something reasonable, vol. 2
Turns out we can utilize this limit at a couple more places, so let's
move the previously defined limit constant to env-util.h and use it to
guard a couple more D-Bus methods. Also, bump it a bit, given it's meant
to be a safety cap that can't be hit in valid scenarios.

Follow-up for 7671b43cb8.
2026-05-01 20:11:53 +02:00
Frantisek Sumsal eb357bfff8 dbus-manager: limit the number of states/patterns per query
Let's cap the number of states/patterns per query to something
reasonable, i.e. max 256 states and 4K patterns per query.
2026-05-01 20:01:57 +02:00
Frantisek Sumsal 7671b43cb8 sd-json: limit the number of env variables to something reasonable
Let's start with 1024, as that should be plenty for all sane use cases.
2026-04-30 09:46:46 +02:00
Frantisek Sumsal 1c534452e9 dns-question: limit the number of questions per query
Let's cap the number of question each query can have to something
reasonable - 128 questions per query should be more than enough for any
real-world scenario.
2026-04-30 09:43:55 +02:00
Frantisek Sumsal 82614a4c6f sd-json,user-record: store the strv size when extending it
So strv_push_with_size() doesn't have to recalculate the size every
time.
2026-04-29 16:33:46 +02:00
Frantisek Sumsal 51a88ac723 core: limit the number of units that can be requested over ListUnitsByNames 2026-04-29 12:58:58 +02:00
Frantisek Sumsal 0d71d58da0 sd-bus: store the strv size when extending it
So strv_push_with_size() doesn't have to recalculate the size every
time.
2026-04-29 12:58:22 +02:00
Frantisek Sumsal bad594f79a ssh-proxy: add a missing dispatch table sentinel
Which was accidentally dropped in e6be5fb720.

Follow-up for e6be5fb720.
2026-04-26 20:59:37 +02:00
Frantisek Sumsal ef3c07352b test: don't strip directives from test units
The original find was matching even our test units, which caused issues
when the check was extended with Memory*= directives, as we stripped
them off from test units for TEST-55-OOMD where we certainly need them.
Since the stripping was meant primarily for "production-grade" units,
let's limit it to units under /etc/systemd/system/ and
/usr/lib/systemd/system/.
2026-04-25 23:39:47 +02:00
Frantisek Sumsal 035ba3ea57 test: slightly reduce the performance/memory overhead for wrapped binaries
Let's drop the quarantine that ASan uses for use-after-free detection,
as it's pointless in wrapped binaries and can consume up to 256 MiB of
memory (with the default configuration). Also, don't keep any stack
traces for allocations & deallocations, which should (slightly) help
with both memory & performance overhead.
2026-04-25 18:55:47 +02:00
Frantisek Sumsal 445f980548 test: temporarily ignore sanitizer warning about blocked ptrace()
LLVM 22 introduced an additional check [0] for ptrace() syscall when
invoking sanitizers [0] which currently produces a false-positive
warning when running some of our units under sanitizers:

[   47.524680] systemd-timedated[740]: ==740==WARNING: ptrace appears to be blocked (is seccomp enabled?). LeakSanitizer may hang.
[   47.524680] systemd-timedated[740]: ==740==Child exited with signal 15.
...
[ 1555.734223] systemd-oomd[93]: ==93==WARNING: ptrace appears to be blocked (is seccomp enabled?). LeakSanitizer may hang.
[ 1555.734223] systemd-oomd[93]: ==93==Child exited with signal 15.
...

It is a false positive because we disable the seccomp filters
system-wide for our units in the sanitizer jobs.

Now, from what I've seen so far this happens only in
Type=notify(-reload) units that also utilize bus_event_loop_with_idle().
This, combined with the fact that the ptrace()-check child process from
[0] checks only if the child process was killed by _any_ signal, means
that if the systemd unit exits on its own after becoming idle and then
something sends it SIGTERM (either via explicit `systemctl stop` or
during system shutdown), this SIGTERM might hit the ptrace()-check child
process from the sanitizer handler (as we also send the signal to all
processes in the target cgroup), which the parent process then
mistakenly evaluates as a blocked ptrace() syscall, even though the
check process wasn't killed by SIGSYS.

I filed this as [1] to the LLVM project, but let's also temporarily
ignore the warning in the sanitizer report processing, as it currently
causes annoying test fails.

[0] https://github.com/llvm/llvm-project/commit/a708b4bf21d7c2298224cdacf7d424abc3c8fed4
[1] https://github.com/llvm/llvm-project/issues/193714
2026-04-25 18:49:40 +02:00
Frantisek Sumsal e3aaf3d76e test: drop any memory limits from units when running with sanitizers
As the memory usage under sanitizers is quite unpredictable.

This is currently relevant mainly for Polkit, as it introduced memory
limits for its polkitd.service unit in the latest version [0] which are
very easy to trigger when running under sanitizers (as polkitd depends
on libsystemd which brings ASan into polkitd's address space).

[0] https://github.com/polkit-org/polkit/commit/7d9c06c58a957ee3f2a4383ade6f207b05207e3e
2026-04-25 18:49:40 +02:00
Frantisek Sumsal a8400c8f1a test: wrap even more binaries when running with sanitizers
Turns out that the util-linux dep on libsystemd caused more fun than I
originally anticipated:

$ lddtree /usr/bin/dfuzzer
dfuzzer => /usr/bin/dfuzzer (interpreter => /lib64/ld-linux-x86-64.so.2)
    libgio-2.0.so.0 => /lib64/libgio-2.0.so.0
        libgmodule-2.0.so.0 => /lib64/libgmodule-2.0.so.0
        libz.so.1 => /lib64/libz.so.1
        libmount.so.1 => /lib64/libmount.so.1
            libblkid.so.1 => /lib64/libblkid.so.1
            libsystemd.so.0 => /lib64/libsystemd.so.0
                libm.so.6 => /lib64/libm.so.6
                    ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2
        libselinux.so.1 => /lib64/libselinux.so.1
            libpcre2-8.so.0 => /lib64/libpcre2-8.so.0
...

Also, the tpm2 utils now depend on libudev through libcurl -> libssh ->
libfido2 dep chain:

$ lddtree /usr/bin/tpm2_pcrread
tpm2_pcrread => /usr/bin/tpm2_pcrread (interpreter => /lib64/ld-linux-x86-64.so.2)
    ...
    libcurl.so.4 => /lib64/libcurl.so.4
    ...
        libssh.so.4 => /lib64/libssh.so.4
            libfido2.so.1 => /lib64/libfido2.so.1
                libcbor.so.0.13 => /lib64/libcbor.so.0.13
                libudev.so.1 => /lib64/libudev.so.1
                    libgcc_s.so.1 => /lib64/libgcc_s.so.1
...

Follow-up for 8030e0b19e.
2026-04-25 17:42:20 +02:00
Frantisek Sumsal 8030e0b19e test: wrap mount/umount when running with sanitizers
On Fedora Rawhide mount/umount is linked against libsystemd, which then
breaks the binaries in sanitizer runs, as we try to run instrumented
code from an uninstrumented binary:

bash-5.3# ldd /usr/bin/mount
        linux-vdso.so.1 (0x00007fa757ef9000)
        libmount.so.1 => /lib64/libmount.so.1 (0x00007fa757e84000)
        libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fa757e51000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fa757c56000)
        libblkid.so.1 => /lib64/libblkid.so.1 (0x00007fa757c16000)
        libsystemd.so.0 => /lib64/libsystemd.so.0 (0x00007fa757400000)
        libpcre2-8.so.0 => /lib64/libpcre2-8.so.0 (0x00007fa75734f000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fa757efb000)
        libclang_rt.asan.so => /usr/lib/clang/22/lib/x86_64-redhat-linux-gnu/libclang_rt.asan.so (0x00007fa756800000)
        libm.so.6 => /lib64/libm.so.6 (0x00007fa7566e4000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fa7566b7000)
        libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007fa756400000)
bash-5.3# mount
==458==ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.

This then breaks the whole machine, as mount is quite essential during
boot.

Let's just add mount/umount to the list of wrapped binaries to fix this.
2026-04-22 22:29:55 +01:00
Frantisek Sumsal 4d92c72b81 test: avoid using external commands in trap handlers
In #39675 the reported fail was as follows:

5580s [  247.559994] TEST-13-NSPAWN.sh[1858]: Exported 93%.
5580s [  247.659002] TEST-13-NSPAWN.sh[1858]: Exported 95%.
5580s [  247.785893] TEST-13-NSPAWN.sh[1858]: Operation completed successfully.
5580s [  247.923727] TEST-13-NSPAWN.sh[1858]: Exiting.
5580s [  258.300406] TEST-13-NSPAWN.sh[1074]: + machinectl import-raw /var/tmp/container-export.raw container-raw-reimport
5580s [  258.323328] TEST-13-NSPAWN.sh[1884]: The 'machinectl import-raw' command has been replaced by 'importctl -m import-raw'. Redirecting invocation.
5580s [  258.659982] TEST-13-NSPAWN.sh[1884]: Failed to transfer image: Remote peer disconnected
5580s [  258.734218] TEST-13-NSPAWN.sh[1074]: + at_exit

Turns out that the real reason behind this fail is that the machine was
under heavy load due to a busy-loop from the stub init. The cause of
this is a bug in bash, where running commands that fork (i.e. not
built-ins) can cause a permanent busy-loop due to a desync in trap
handling if you send the signals to the bash process _just right_:

[   90.855318] TEST-13-NSPAWN.sh[1074]: + machinectl poweroff long-running long-running long-running
[   90.855318] TEST-13-NSPAWN.sh[1074]: + machinectl reboot long-running long-running long-running
[   90.928980] systemd-nspawn[1679]: ++ touch /poweroff
[   90.928980] systemd-nspawn[1679]: +++ touch /reboot
[   90.928980] systemd-nspawn[1679]: + :
[   90.928980] systemd-nspawn[1679]: + :
[   90.928980] systemd-nspawn[1679]: + wait
[   90.928980] systemd-nspawn[1679]: + :
[   90.928980] systemd-nspawn[1679]: + :
[   90.928980] systemd-nspawn[1679]: + wait
[   90.928980] systemd-nspawn[1679]: + :
[   90.928980] systemd-nspawn[1679]: + :
[   90.928980] systemd-nspawn[1679]: + wait
...

$ journalctl --file TEST-13-NSPAWN-1.journal -o short-monotonic --no-hostname --grep "^\+ wait$" | wc -l
349734

So the stub-init was hammering the machine in a tight endless loop,
which then caused systemd-importd to timeout when talking to D-Bus:

[  258.300096] TEST-13-NSPAWN.sh[1074]: + machinectl import-raw /var/tmp/container-export.raw container-raw-reimport
...
[  258.415319] systemd-importd[1859]: Unable to request name, failing connection: Method call timed out
[  258.483662] systemd-importd[1859]: Bus n/a: changing state RUNNING → CLOSING
[  258.605442] systemd-importd[1859]: Bus n/a: changing state CLOSING → CLOSED
[  258.659958] TEST-13-NSPAWN.sh[1884]: Failed to transfer image: Remote peer disconnected

Given this is not our issue, let's work around it by using just
built-ins from the trap handlers, which are not susceptible to this bug.

Resolves: #39675
2026-04-21 16:19:47 +01:00
Frantisek Sumsal 1b787f20cf test: convert sd-journal tests to the new test macros
So we can, hopefully, debug issues like #40551 more easily.
2026-04-20 19:43:26 +01:00
Frantisek Sumsal c955e24916 test: re-enable sync in TEST-25-IMPORT
Newer tar started using openat2() via open_subdir() to address
CVE-2025-45582 [0]. Now, gnulib, that tar uses, provides the openat2()
syscall in two ways [1]:

1) If glibc doesn't provide openat2(), it provides its own version in
   openat2.c, that tries to call openat2() syscall first, and if it
   returns ENOSYS, it emulates the function in userspace.

2) If glibc provides openat2(), it uses that directly, without providing
   any fallback on ENOSYS.

Quite recently our test suite started calling nspawn with
--suppress-sync=yes. This means that we call seccomp_suppress_sync(),
which eventually calls block_open_flag(), that blocks the openat2()
syscall completely and refuses it with ENOSYS as this syscall can't be
sensibly filtered (see the openat2()-relevant comments in
block_open_flag() and seccomp_restrict_sxid()). And when glibc provides
openat2(), there's no fallback, so the ENOSYS bubbles up to the user as:

TEST-25-IMPORT.sh[163]: + tar xzf /var/tmp/scratch.tar.gz
TEST-25-IMPORT.sh[163]: tar: ./adirectory/athirdfile: Cannot open: Function not implemented
TEST-25-IMPORT.sh[163]: tar: Exiting with failure status due to previous errors

Let's mitigate this by re-enabling sync for TEST-25-IMPORT, at least for
now.

[0] https://cgit.git.savannah.gnu.org/cgit/tar.git/commit/?id=75b03fdff48916bd0654677ed21379bdb0db016d
[1] https://cgit.git.savannah.gnu.org/cgit/gnulib.git/commit/?id=0b97ffdf32bdab909d02449043447237273df75e
2026-04-20 14:33:36 +01:00
Frantisek Sumsal 3297add538 compress: simplify the condition a bit
Simply mirror the format we've already established in
decompress_blob_zstd().
2026-04-20 09:37:13 +02:00
Frantisek Sumsal 35eb598af2 compress: gracefully handle a truncated ZSTD frame
If a journal file contains a truncated ZSTD frame (i.e. a frame with
Frame_Content_Size > 0, but with not enough data in Data_Block),
ZSTD_decompressStream() would return a non-zero, non-error value. This
would then skip the error path in the ZSTD_isError() branch and we'd hit
the following assert:

$ build-local/journalctl -o cat --file zstd-truncated.journal
Assertion 'output.pos >= prefix_len + 1' failed at src/basic/compress.c:1236, function decompress_startswith_zstd(). Aborting.
Aborted                    (core dumped) build-local/journalctl -o cat --file zstd-truncated.journal

Let's handle this situation gracefully and return EBADMSG instead.

Also, add another journalctl invocation to the corrupted-journals test
that goes through the sd_journal_get_data() -> decompress_startswith_zstd()
code path which, among other things, covers the issue when run on the
provided journal file.
2026-04-20 09:37:13 +02:00
Frantisek Sumsal e869c83367 test: append .journal to unpacked corrupted journals
Otherwise `journalctl --directory=` skips over them in the second part of
the test.
2026-04-20 09:37:13 +02:00
Frantisek Sumsal 6182d0c666 import: fix an always-true assert() 2026-04-19 14:06:30 +02:00
Frantisek Sumsal 774a9f440b strxcpyx: add a paranoia check for vsnprintf()'s return value
vsnprintf() can, under some circumstances, return negative value, namely
during encoding errors when converting wchars to multi-byte characters.
This would then wreak havoc in the arithmetics we do following the
vsnprintf() call. However, since we never do any wchar shenanigans in
our code it should never happen.

Let's encode this assumption into the code as an assert(), similarly how
we already do this in other places (like strextendf_with_separator()).
2026-04-19 14:06:15 +02:00
Frantisek Sumsal c859d41232 namespace: don't log misleading error in the r > 0 path
fd_is_fs_type() returns < 0 for errors, 0 for false, and > 0 for true, so
in the r > branch we'd most likely report EPERM together with the error
message which is misleading.
2026-04-17 15:54:44 +02:00
Frantisek Sumsal d651df8283 nspawn: avoid passing NULL to log_syntax()
If range is NULL (i.e. when PrivateUsers= doesn't contain ':'),
both later error paths will then pass NULL to log_syntax():

~# cat foo.nspawn
[Exec]
PrivateUsers=9999999999999999999

~# SYSTEMD_LOG_LEVEL=debug systemd-nspawn -D foo |& grep foo.nspawn
Found settings file: /root/foo.nspawn
/root/foo.nspawn:2: UID/GID shift invalid, ignoring: (null)

or

~# cat foo.nspawn
[Exec]
PrivateUsers=4294967294

~ # SYSTEMD_LOG_LEVEL=debug systemd-nspawn -D foo |& grep foo.nspawn
Found settings file: /root/foo.nspawn
/root/foo.nspawn:2: UID/GID shift and range combination invalid, ignoring: (null)

Let's just use rvalue in both of these cases instead.
2026-04-17 14:55:29 +02:00
Frantisek Sumsal 4977c00e2a udev-builtin: add a couple of asserts 2026-04-17 12:37:30 +02:00
Frantisek Sumsal 5853d0a533 scsi_id: use safe_atoi() instead of plain atoi() 2026-04-17 12:37:30 +02:00
Frantisek Sumsal 376d5ccba1 dissect-image: fix path building for non-raw images
If the passed in image path didn't end with .raw, we'd return an empty
string + suffix instead of the intended image + suffix path.

Follow-up for 89e62e0bd3.
2026-04-17 12:04:44 +02:00
Frantisek Sumsal 335cc8f39c timesync: verify the actual size of the received data
iov.iov_len doesn't change after calling recvmsg() so it remains set to
sizeof(ntpmsg), which makes the check for a short packet always false.
Let's fix that by checking the actual size of the received data instead.
2026-04-16 19:19:02 +01:00
Frantisek Sumsal b22daa97e1 string-util: check for overflow in strrep()
This simply mirrors the same overflow check we already have in
strrepa(), in case someone passed us a sufficiently long string.

strrep() is currently used only in tests, so this is just hardening.
2026-04-16 17:46:31 +02:00
Frantisek Sumsal 31d360fb0b journal: limit decompress_blob() output to DATA_SIZE_MAX
We already have checks in place during compression that limit the data
we compress, so they shouldn't decompress to anything larger than
DATA_SIZE_MAX unless they've been tampered with. Let's make this
explicit and limit all our decompress_blob() calls in journal-handling
code to that limit.

One possible scenario this should prevent is when one tries to open and
verify a journal file that contains a compression bomb in its payload:

$ ls -lh test.journal
-rw-rw-r--+ 1 fsumsal fsumsal 1.2M Apr 12 15:07 test.journal

$ systemd-run --user --wait --pipe -- build-local/journalctl --verify --file=$PWD/test.journal
Running as unit: run-p682422-i4875779.service
000110: Invalid hash (00000000 vs. 11e4948d73bdafdd)
000110: Invalid object contents: Bad message
File corruption detected at /home/fsumsal/repos/@systemd/systemd/test.journal:272 (of 1249896 bytes, 0%).
FAIL: /home/fsumsal/repos/@systemd/systemd/test.journal (Bad message)
          Finished with result: exit-code
Main processes terminated with: code=exited, status=1/FAILURE
               Service runtime: 48.051s
             CPU time consumed: 47.941s
                   Memory peak: 8G (swap: 0B)

Same could be, in theory, possible with just `journalctl --file=`, but
the reproducer would be a bit more complicated (haven't tried it, yet).

Lastly, the change in journal-remote is mostly hardening, as the maximum
input size to decompress_blob() there is mandated by MHD's connection
memory limit (set to JOURNAL_SERVER_MEMORY_MAX which is 128 KiB at the
time of writing), so the possible output size there is already quite
limited (e.g. ~800 - 900 MiB for xz-compressed data).
2026-04-12 16:28:15 +02:00
Frantisek Sumsal 2cda5f6169 compress: limit the output to dst_max bytes with LZ4 if set
We already do that with other algorithms, so let's make
decompress_blob_lz4() consistent with the rest.
2026-04-12 16:28:15 +02:00
Frantisek Sumsal d830bb1fc1 journal: move the {DATA,ENTRY}_SIZE constants to sd-journal
So we can access them from the code there as well.
2026-04-12 16:10:03 +02:00
Frantisek Sumsal d9da339bf1 sd-varlink: scale down the limit of connections per UID to 128
1024 connections per UID is unnecessarily generous, so let's scale this
down a bit. D-Bus defaults to 256 connections per UID, but let's be even
more conservative and go with 128.
2026-04-12 13:57:39 +02:00
Frantisek Sumsal 1016dd315f sd-json: limit the stack depth during parsing as well 2026-04-10 21:33:11 +01:00
Frantisek Sumsal 92d87ac302 sd-bus: don't overallocate the message buffer
newa(t, n) already allocates sizeof(t) * n bytes, so previously we'd
actually allocate sizeof(t) * sizeof(t) * n bytes, which is ~16x more
(on x86_64) that we actually needed.

This is probably an oversight from a tree-wide change in
6e9417f5b4 that replaced alloca() with
newa().

Follow-up for 6e9417f5b4.
2026-04-10 12:20:16 +01:00
Frantisek Sumsal d3a1710bc2 sd-varlink: fix a potential connection count leak
With the old version there was a potential connection count leak if
either of the two hashmap operations in count_connection() failed. In
that case we'd return from sd_varlink_server_add_connection_pair()
_before_ attached the sd_varlink_server object to an sd_varlink object,
and since varlink_detach_server() is the only place where the connection
counter is decremented (called through sd_varlink_close() in various
error paths later _if_ the "server" object is not null, i.e. attached to
the sd_varlink object) we'd "leak" a connection every time this
happened. However, the potential of abusing this is very theoretical,
as one would need to hit OOM every time either of the hashmap operations
was executed for a while before exhausting the connection limit.

Let's just increment the connection counter after any potential error
path, so we don't have to deal with potential rollbacks.
2026-04-09 22:05:25 +01:00
Frantisek Sumsal 44d0f273fa portablectl: fix swapped arguments for setns()
Follow-up for 824fcb95c9.
2026-04-09 20:43:42 +01:00
Frantisek Sumsal 1e551f0d96 sd-varlink: check flags against the correct field
Otherwise even a method without SD_VARLINK_SUPPORTS_MORE/REQUIRES_MORE
can emit "continues" replies without our IDL validation catching it.
2026-04-08 21:33:05 +01:00
Frantisek Sumsal 3d5bd67a22 fstab-generator: support swap on network block devices
Teach swap units to support the _netdev option as well, which should
make swaps on iSCSI possible. This mirrors the logic we already have for
regular mounts in both the fstab-generator and the core
(mount.c/swap.c).

Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 11:51:09 +01:00
Frantisek Sumsal f912de9312 homectl: apply all --member-of= groups from a comma-separated list
Commit 0e1ede4b4b introduced a bug where
we'd always fetch the "original" (empty) list of groups when processing
a comma-separated list of groups from the --member-of= option, so only
the last group from the list would get applied. This bug was then later
(in 316e9887f2) refactored into a separate
function.

Follow-up for 0e1ede4b4b.
Resolves: #41286
2026-03-25 10:41:06 +01:00
Frantisek Sumsal 924f5a3461 sd-varlink: gracefully reject arrays/maps with a null element
Follow-up for 799392286e.
2026-03-22 19:37:47 +01:00
Frantisek Sumsal 6dcabd5f5e coccinelle: simplify file exclusions
Use Coccinelle's "depends on" directive to exclude files from certain
transformations. This should make them a bit simpler and possibly
faster, since we don't have to shell out to Python.

Unfortunately, this works only for file/directory exclusions. For
function and other more complex exclusions we still need to use Python,
at least for now.

Also, completely drop the file exclusion for man/ in the xsprintf
transformation, since we filter out everything under man/ before we even
run Coccinelle (in run-coccinelle.sh).
2026-03-14 23:56:53 +00:00
Frantisek Sumsal be0db50cad fuzz: add a fuzzer for user records
Add a simple fuzzer that verifies our machinery for parsing user records
from JSON works as intended.

The initial corpus was created with the help of Claude, so we have a
bunch of valid user records with as much fields as possible for the
initial corpus.
2026-03-06 19:03:14 +01:00
Frantisek Sumsal ad1c7df5f9 man: fix short option for userdbctl's --from-file= 2026-03-06 18:39:07 +01:00
Frantisek Sumsal 3c7bd947b2 shared: don't leak memory from array fields
The fido2_hmac_salt/fido2_hmac_credential/recovery_key fields kept
leaking memory as the array itself wasn't deallocated after deallocating
each of its elements data:

$ build-san/userdbctl -F fuzz-corpus-userdb/auth-fido2.json
...
=================================================================
==1292840==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 112 byte(s) in 1 object(s) allocated from:
    #0 0x7f56f00e5e4b in realloc.part.0 (/lib64/libasan.so.8+0xe5e4b) (BuildId: 25975f766867e9e604dc5a71a8befeaed3301942)
    #1 0x7f56ed869e42 in greedy_realloc ../src/basic/alloc-util.c:65
    #2 0x7f56ed7ff5e9 in dispatch_fido2_hmac_salt ../src/shared/user-record.c:836
    #3 0x7f56edd73cbc in sd_json_dispatch_full ../src/libsystemd/sd-json/sd-json.c:5204
    #4 0x7f56edd745fc in sd_json_dispatch ../src/libsystemd/sd-json/sd-json.c:5276
    #5 0x7f56ed80100b in dispatch_privileged ../src/shared/user-record.c:998
    #6 0x7f56edd73cbc in sd_json_dispatch_full ../src/libsystemd/sd-json/sd-json.c:5204
    #7 0x7f56edd745fc in sd_json_dispatch ../src/libsystemd/sd-json/sd-json.c:5276
    #8 0x7f56ed80622c in user_record_load ../src/shared/user-record.c:1697
    #9 0x000000408c15 in display_user ../src/userdb/userdbctl.c:447
    #10 0x7f56ed83cc9a in dispatch_verb ../src/shared/verbs.c:137
    #11 0x00000041df2b in run ../src/userdb/userdbctl.c:1908
    #12 0x00000041dfbe in main ../src/userdb/userdbctl.c:1911
    #13 0x7f56ec8105b4 in __libc_start_call_main (/lib64/libc.so.6+0x35b4) (BuildId: 2b5beec0fd24fe9c9f43eddfdd5facf0b8a1b805)
    #14 0x7f56ec810667 in __libc_start_main@@GLIBC_2.34 (/lib64/libc.so.6+0x3667) (BuildId: 2b5beec0fd24fe9c9f43eddfdd5facf0b8a1b805)
    #15 0x000000404a44 in _start (/home/fsumsal/repos/@systemd/systemd/build-san/userdbctl+0x404a44) (BuildId: 19e8b7e7b7038d2cea20bc18a55bea2a9e4406d5)

Direct leak of 64 byte(s) in 1 object(s) allocated from:
    #0 0x7f56f00e5e4b in realloc.part.0 (/lib64/libasan.so.8+0xe5e4b) (BuildId: 25975f766867e9e604dc5a71a8befeaed3301942)
    #1 0x7f56ed869e42 in greedy_realloc ../src/basic/alloc-util.c:65
    #2 0x7f56ed7fe779 in dispatch_fido2_hmac_credential_array ../src/shared/user-record.c:775
    #3 0x7f56edd73cbc in sd_json_dispatch_full ../src/libsystemd/sd-json/sd-json.c:5204
    #4 0x7f56edd745fc in sd_json_dispatch ../src/libsystemd/sd-json/sd-json.c:5276
    #5 0x7f56ed80622c in user_record_load ../src/shared/user-record.c:1697
    #6 0x000000408c15 in display_user ../src/userdb/userdbctl.c:447
    #7 0x7f56ed83cc9a in dispatch_verb ../src/shared/verbs.c:137
    #8 0x00000041df2b in run ../src/userdb/userdbctl.c:1908
    #9 0x00000041dfbe in main ../src/userdb/userdbctl.c:1911
    #10 0x7f56ec8105b4 in __libc_start_call_main (/lib64/libc.so.6+0x35b4) (BuildId: 2b5beec0fd24fe9c9f43eddfdd5facf0b8a1b805)
    #11 0x7f56ec810667 in __libc_start_main@@GLIBC_2.34 (/lib64/libc.so.6+0x3667) (BuildId: 2b5beec0fd24fe9c9f43eddfdd5facf0b8a1b805)
    #12 0x000000404a44 in _start (/home/fsumsal/repos/@systemd/systemd/build-san/userdbctl+0x404a44) (BuildId: 19e8b7e7b7038d2cea20bc18a55bea2a9e4406d5)

SUMMARY: AddressSanitizer: 176 byte(s) leaked in 2 allocation(s).
2026-03-06 18:39:07 +01:00
Frantisek Sumsal 76ab7861ff shared: don't exclude valid min/max values for cgroup weight fields
1 and 10000 are valid cgroup weight values, but the condition was
incorrectly excluding them:

$ echo '{"userName":"crashhostarray","cpuWeight":1}' | userdbctl -F -
<stdin>:1:42: JSON field 'cpuWeight' is not in valid range 1…10000.

$ echo '{"userName":"crashhostarray","cpuWeight":10000}' | userdbctl -F -
<stdin>:1:42: JSON field 'cpuWeight' is not in valid range 1…10000.
2026-03-06 18:39:01 +01:00
Frantisek Sumsal 1e2517bf2e shared: fix segfault when processing matchHostname field
Fix a typo which causes a segfault when processing a user record
with matchHostname when it's an array instead of a simple string:

$ echo '{"userName":"crashhostarray","perMachine":[{"matchHostname":["host1","host2"],"locked":false}]}' | userdbctl -F -
Segmentation fault         (core dumped)

$ coredumpctl info
...
       Message: Process 1172301 (userdbctl) of user 1000 dumped core.

                Module libz.so.1 from rpm zlib-ng-2.3.3-1.fc43.x86_64
                Module libcrypto.so.3 from rpm openssl-3.5.4-2.fc43.x86_64
                Stack trace of thread 1172301:
                #0  0x00007fded7b3a656 __strcmp_evex (libc.so.6 + 0x159656)
                #1  0x00007fded7e95397 per_machine_hostname_match (libsystemd-shared-260.so + 0x295397)
                #2  0x00007fded7e955b5 per_machine_match (libsystemd-shared-260.so + 0x2955b5)
                #3  0x00007fded7e957c6 dispatch_per_machine (libsystemd-shared-260.so + 0x2957c6)
                #4  0x00007fded7e96c97 user_record_load (libsystemd-shared-260.so + 0x296c97)
                #5  0x000000000040572d display_user (/home/fsumsal/repos/@systemd/systemd/build/userdbctl + 0x572d)
                #6  0x00007fded7ea9727 dispatch_verb (libsystemd-shared-260.so + 0x2a9727)
                #7  0x000000000041077c run (/home/fsumsal/repos/@systemd/systemd/build/userdbctl + 0x1077c)
                #8  0x00000000004107ce main (/home/fsumsal/repos/@systemd/systemd/build/userdbctl + 0x107ce)
                #9  0x00007fded79e45b5 __libc_start_call_main (libc.so.6 + 0x35b5)
                #10 0x00007fded79e4668 __libc_start_main@@GLIBC_2.34 (libc.so.6 + 0x3668)
                #11 0x00000000004038d5 _start (/home/fsumsal/repos/@systemd/systemd/build/userdbctl + 0x38d5)
                ELF object binary architecture: AMD x86-64
2026-03-06 17:24:53 +01:00
Frantisek Sumsal 45830bc8d2 test: drop some extraneous whitespaces 2026-03-06 16:09:21 +01:00
Frantisek Sumsal ab73333c43 core: drop unused errno from debug message
And properly guard unset parameters.
2026-03-05 10:27:59 +01:00
Frantisek Sumsal 90f5d2a202 tree-wide: various fixlets suggested by Coccinelle 2026-03-05 10:27:58 +01:00
Frantisek Sumsal a8335fb91f tree-wide: use typed SD_JSON_BUILD_PAIR_* macros more 2026-03-05 10:27:58 +01:00
Frantisek Sumsal 41d9419562 coccinelle: work around a bug in zlib.h parsing
Currently, parsing zlib.h on Fedora (and possibly others) causes spatch
to fail with an assertion. Let's work around that by defining two extra
macros in our Coccinelle parsing hacks.
2026-03-05 10:27:58 +01:00
Frantisek Sumsal 77ee36a740 coccinelle: simplify the SD_JSON_BUILD_PAIR_* transformations
And also disable them on test-json.c, since there we use the macros
intentionally in a "non-optimal" way to actually test them.
2026-03-05 10:27:58 +01:00
Frantisek Sumsal f8d0ab46cf coccinelle: update the list of excluded directories
- src/boot/efi/ was moved to src/boot/ in 97318131fd
  - src/basic/include/linux/ was moved to src/include/uapi/linux/ in
    1a60b97524
2026-03-05 10:27:58 +01:00
Frantisek Sumsal 86867d1240 coccinelle: ignore our own BPF programs
Since they don't have access to systemd code, so we can't use our custom
functions/macros in them anyway.
2026-03-05 10:27:58 +01:00
Frantisek Sumsal acf00ba7b5 test: don't register short-living containers with machined, again
Otherwise we might try to register the same scope again before the
previous instance gets a chance to be cleaned up:

[   54.378392] systemd-nspawn[2554]: ░ Spawning container TEST-13-NSPAWN.defaultinaccessiblepaths.nxs on /var/lib/machines/TEST-13-NSPAWN.default_inaccessible_paths.nxs.
[   54.382202] systemd-nspawn[2554]: Failed to allocate scope: Unit TEST-13-NSPAWN.defaultinaccessiblepaths.nxs.scope was already loaded or has a fragment file.
[   54.411211] systemd[1]: TEST-13-NSPAWN.service: Main process exited, code=exited, status=1/FAILURE
[   54.411413] systemd[1]: TEST-13-NSPAWN.service: Failed with result 'exit-code'.
[   54.411885] systemd[1]: Failed to start TEST-13-NSPAWN.service - TEST-13-NSPAWN.

This is basically the same change as in
6a05abb9b4 but for the newly added tests.

Follow-up for 83b8daa032.

Resolves: #40945
2026-03-04 13:03:15 +00:00
Frantisek Sumsal 864c08f575 import,nspawn: fix a couple of typos in mountfsd 2026-03-03 00:03:19 +01:00
Frantisek Sumsal 83b8daa032 nspawn: actually mask certain files under /proc/
/run/systemd/inaccessible/ exists only on host - in the container we have
/run/host/inaccessible/, and since all the inaccessible mounts have
MOUNT_IN_USERNS we need to use the latter one, otherwise the masking
gets silently skipped:

~# SYSTEMD_LOG_LEVEL=debug systemd-nspawn -q --directory=foo ls -la /proc/kallsyms
...
Bind-mounting /run/systemd/inaccessible/reg on /proc/kallsyms (MS_BIND "")...
Failed to mount /run/systemd/inaccessible/reg (type n/a) on /proc/kallsyms (MS_BIND ""): No such file or directory
Changing mount flags /proc/kallsyms (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_REMOUNT|MS_BIND "")...
Failed to mount n/a (type n/a) on /proc/kallsyms (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_REMOUNT|MS_BIND ""): Invalid argument
Bind-mounting /run/systemd/inaccessible/reg on /proc/kcore (MS_BIND "")...
Failed to mount /run/systemd/inaccessible/reg (type n/a) on /proc/kcore (MS_BIND ""): No such file or directory
Changing mount flags /proc/kcore (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_REMOUNT|MS_BIND "")...
Failed to mount n/a (type n/a) on /proc/kcore (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_REMOUNT|MS_BIND ""): Invalid argument
...
Inner child finished, invoking payload.
-r--r--r--. 1 root root 0 Feb 25 13:19 /proc/kallsyms
2026-03-02 20:22:06 +01:00
Frantisek Sumsal ade882f16f sd-journal: fix a copy-paste error
Dereference the Directory pointer in the debug message instead of the
(probably already freed) JournalFile one.
2025-11-26 00:36:03 +01:00
Frantisek Sumsal 3605b3ba87 timer: rebase last_trigger timestamp if needed
After bdb8e584f4 we stopped rebasing the
next elapse timestamp unconditionally and the only case where we'd do
that was when both last trigger and last inactive timestamps were empty.
This covered timer units during boot just fine, since they would have
neither of those timestamps set. However, persistent timers
(Persistent=yes) store their last trigger timestamp on a persistent
storage and load it back after reboot, so the rebasing was skipped in
this case.

To mitigate this, check the last_trigger timestamp is older than the
current machine boot - if so, that means that it came from a stamp file
of a persistent timer unit and we need to rebase it to make
RandomizedDelaySec= work properly.

Follow-up for bdb8e584f4.

Resolves: #39739
2025-11-20 09:47:11 +01:00