The color list is extended by searching, suggestions from llms, etc.
In particular, I included "-ish" variants of common colors: "greenish",
"reddish", etc. They are easy to identify and I think quite distinct from
the base versions.
For weather words, add more stuff. Some are not commonly used, but mostly
easy to spell. In the worst case, people learn more about dreary weather.
For worlds, add a bunch of solar system moons, asteroids, trans-Neptunian
objects, and mythological realms.
This raises the number of combinations from 68'880 to appox. 2'300'000,
making collisions and repeats less likely.
Split out of https://github.com/systemd/systemd/pull/42983
## Problem
`dns_packet_append_name()` fails with `-EEXIST` once a name that was
first appended at an offset beyond RFC 1035's 14-bit compression-pointer
range (0x4000) occurs again in the same packet: the fall-through that
re-emits the labels trips `hashmap_ensure_put()` on the existing map
entry, and the whole packet build fails — reported by the caller as, for
example, "Failed to build reply packet: File exists".
The map is meant to hold only offsets a pointer can actually reference.
Names past that range were being inserted anyway, so they were dead
weight that could never be pointed at, and re-encountering one turned
into an error.
## Fix
Keep names at offsets ≥ 0x4000 out of the compression map entirely.
"Mapped implies referenceable" becomes an invariant of the map rather
than something callers have to reason about, and the `-EEXIST` collision
cannot arise. A re-occurrence of such a name is emitted as labels, still
compressed from the first suffix that *is* mapped in range — which is
the only encoding RFC 1035 section 4.1.4 permits in that case, since a
pointer must address ≤ 0x3FFF.
## Impact
Any packet large enough to push a name past 16KiB hits this — it is not
mDNS-specific, though that is where it was found: a DNS-SD announcement
covering a few hundred published services crosses the threshold easily,
and every record after it fails to encode.
## Test
`test-dns-packet-append` gains coverage for the boundary: a name first
written past the pointer range and then repeated, the same for a full
resource record, the rollback path when an append fails mid-name, and
the map invariant itself (asserted over the whole map, so a future
change that inserts an unreferenceable offset is caught rather than
silently reintroducing the bug).
## Testing status
Builds clean with `-Dwerror=true`; `meson test --suite resolve` passes
(19 ok, 1 skipped) at both commits.
dns_packet_append_name() appends a name's labels and then the root byte
that terminates it. A failure on that last byte returned directly,
leaving both the labels on the packet and the compression entries this
call had added -- entries naming offsets the caller then truncates away.
Take the same rollback path as every other failure in the function.
Independent of the map invariant fixed before it: this is reachable
whether or not any offset exceeded the pointer range.
The existing case only covered a fully-uncompressed re-occurrence. Every
DNS-SD name shares an in-range suffix, so also pin that a name first
seen beyond the pointer range still compresses from its mapped suffix --
one label plus a two-byte pointer per occurrence, map unchanged, and the
pointer addressing the suffix's prior occurrence rather than any
in-range octet.
A padding helper puts the next append at a chosen offset, for the two
sides of the boundary: at 0x3FFF the name is still mapped and re-occurs
as a pointer whose wire bytes are checked independently of the parser;
one past it, it is not mapped.
dns_packet_append_name() added every name it appends to the packet's
compression map, including names written at offsets >= 0x4000 that an
RFC 1035 compression pointer (14 bits) can never reference. Such
entries are not just useless -- when the same name occurs again, the
lookup finds it, the pointer cannot be emitted, the code falls through
to appending the labels again, and the re-insertion into the map then
fails the whole packet build with hashmap_ensure_put() returning
-EEXIST.
This is easily triggered with mDNS: an unsolicited DNS-SD announcement
covering a zone with a few hundred services exceeds 16KiB, and
dns_scope_announce() then fails with "Failed to build reply packet:
File exists".
This lifts the failure only: dns_scope_announce() still builds the whole
zone into one packet and hands it to the socket as a single datagram,
fragmented as the link requires. Splitting announcements across
MTU-sized packets, as RFC 6762 section 17 asks, is done separately.
Only insert a name into the map if its offset is pointer-expressible,
making "mapped implies referenceable" a map invariant. Re-occurrences of
out-of-range names are emitted as labels again -- the only encoding RFC
1035 section 4.1.4 permits in that case -- still compressed from the
first suffix that is mapped in range.
The lookup restates that range as control flow rather than asserting it.
Under -Db_ndebug=true, which CI builds, assert() is
__builtin_unreachable(), so an out-of-range entry would put 0xC000 | n
truncated to 14 bits on the wire -- a pointer to an arbitrary earlier
offset -- where falling through emits the labels instead. This layer is
shared with unicast DNS, DoT and LLMNR.
The pointer's tag bits and its offset bound are now named once, in
dns-def.h next to the other wire-format constants rather than in
dns-packet.h, since the format is RFC 1035's and not this packet
abstraction's -- dhcp-message.c open-codes the same two bits to parse
compressed names out of DHCP options. Compile-time checks pin that the
two partition the 16-bit field and that the tag is its top two bits,
which is what lets the reader take it from the first octet alone.
The offset's other bound becomes control flow along with it: it was an
assert(), which under -Db_ndebug=true is no check at all, for the bound
whose violation puts a pointer to the wrong bytes on the wire. It is
also tightened to the start of the name being appended rather than the
end of the packet, which is the "prior occurrence" RFC 1035 section
4.1.4 allows a pointer to address and what dns_packet_read_name()
enforces on the way in.
Add regression tests, including one through dns_packet_append_rr() --
the path the reported failure came from, where the name append is nested
inside the rdlength backpatch and two further rollbacks -- and one for
the reader's label-type dispatch, which the renamed constants touch.
Fixes#43645.
`FailureAction=exit` propagates the exit status of the unit's main
process. When there is none, `emergency_action()` left `m->return_value`
at its initial 0, so the manager exited 0 for a unit that failed —
indistinguishable from an orderly shutdown to whoever supervises it.
machinectl clone --read-only ignored the requested read-only mode when
cloning a foreign-UID-owned directory because the unprivileged clone path
did not receive the read_only argument.
Pass the flag through and apply the immutable bit via the already-open
destination directory fd after copying. Keep this best-effort, matching
the ordinary directory clone fallback, so failure to set the attribute
does not turn a completed copy into a reported clone failure.
Add a regression test using foreign-0 rather than a fixed UID. Verify
the cloned image's ReadOnly property, copied contents and ownership, and
that the immutable directory rejects new entries. Run this subtest only
when file attributes and the existing rootless nspawn prerequisites are
available, leaving the other machined tests unchanged.
unprivileged_clone() returns success when mountfsd_make_directory() fails.
Consequently, callers can report a successful clone even though the
destination directory was never created.
Return the original error instead of zero so the failed clone is
reported to the caller.
Add a regression test that places a regular file without an image suffix
at the destination path. Such a file is ignored by image discovery but
prevents directory creation. Check that cloning reports failure without
changing the file, then succeeds after the conflicting file is removed.
The Varlink service enumerates image targets and needs a private mount
namespace while dissecting images. Keep its sandbox in sync with
systemd-sysupdated.service.
Follow-up to 31616d00efFixes#43604
Vacuuming unlinks and deallocates archived journal files. A reader
may have already selected an entry from such a file, so final
validation can fail after candidate selection succeeded.
If the selected file is now unlinked, remove it and retry candidate
selection. Preserve errors from linked files, including the EIO
reported after SIGBUS recovery from a truncated mapping.
network-generator writes generated command line configuration below
the alternate root passed with --root=, but network.* credentials
were copied to fixed host /run paths.
Build the credential target directories with arg_root before passing
them to pick_up_credentials(). This keeps normal boot behavior
unchanged while making credential output land in the requested root.
manager_recv() sizes its buffer with next_datagram_size_fd(), which peeks at
the datagram with MSG_PEEK|MSG_TRUNC. That peek makes the kernel verify the UDP
checksum - which is the whole point of using it over FIONREAD - and hence also
drop the datagram when the checksum is bad. The datagram that woke the event
source can thus be gone by the time we peek at it, and the peek returns EAGAIN.
manager_recv() returned that error, while the recvmsg_safe() below, which can
fail the same way, already treats it as "no packet". The callers on the mDNS,
LLMNR and stub listening sockets pass a negative return on to the event loop,
which disables the event source, so a single datagram with a bad checksum -
something anyone on the link can send - stops resolved from receiving on that
socket for good. on_dns_packet() completes the transaction with -EAGAIN instead
of retrying it.
So return 0 on a transient error here too, like the other users of the helper
on an event source do. Disconnect errors are left as they are: they show up
here from ICMP errors on the connected UDP socket, and on_dns_packet() handles
them.
Fixes#43442.
A `.swap` unit can end up **active but unloadable, with no device**, and
stopping such a unit aborts PID 1.
### What it looks like
```
$ systemctl show 'dev-disk-byx2dpartlabel-primary.swap' -p LoadState,ActiveState,What
LoadState=not-found
ActiveState=active
What= # <-- no device
$ systemctl stop 'dev-disk-byx2dpartlabel-primary.swap'
systemd[1]: About to execute: /usr/sbin/swapoff # <-- no argument
systemd[1]: Caught <ABRT>, from our own process.
systemd[1]: Freezing execution.
```
PID 1 stays alive but frozen, so the machine stops answering `systemctl`
and never reboots.
### How it happens
1. systemd creates one `.swap` unit per udev device link of every device
in `/proc/swaps`.
2. A device link gets repointed at another device while the swap stays
on. `/proc/swaps` doesn't change, so nothing re-examines the unit — it
stays active.
3. The next `daemon-reload` drops all units and rebuilds them.
Enumeration from `/proc/swaps` no longer produces this name, so only the
deserialization brings the unit back: by name, with no fragment on disk.
4. `swap_load()` only calls `swap_add_extras()` for units that loaded a
fragment or are backed by `/proc/swaps`, so this one gets neither a
`What=` nor a patched `ExecContext`.
5. `swap_coldplug()` restores its active state anyway.
6. Shutdown stops it. `unit_stop()` deliberately permits stopping units
whose load failed, so it reaches `exec_spawn()` and trips the
`PrivateVarTmp=` assertion in `exec_context_serialize()`.
FailureAction=exit propagates the exit status of the unit's main process. When
there is none, emergency_action() left m->return_value at its initial 0, so the
manager exited 0 for a unit that failed. Whoever supervises it, e.g. a container
manager, then can't tell this apart from an orderly shutdown.
There is no status to propagate whenever the unit type has no notion of a main
process (only .service has), or the unit failed before any of its processes ran,
say because its cgroup couldn't be created. StartLimitAction= and
JobTimeoutAction= never have one at all.
Fixes: https://github.com/systemd/systemd/issues/43645
When --sign-initrd-pcrs is used with --policy-digest, generate the
extra enter-initrd policy with the "initrd" policy reference. This
matches the policy produced when ukify signs with a private key.
Fixes building UKIs with offline signing (e.g.: on OBS) and using
NVPCRs.
Follow-up for f0f3687700
systemd-networkd needs authorization for
`org.freedesktop.ModemManager1.Device.Control` to allow establishing a
connection:
systemd-networkd[1457]: ModemManager: starting simple connect on huawei E3276 interface wwp106s0u4i2
systemd-networkd[1457]: Could not connect modem huawei E3276: Unauthorized: PolicyKit authorization failed: not authorized for 'org.freedesktop.ModemManager1.Device.Control'
mdns_maintenance_query_complete() takes a reference on
query->dnsservice_request and never drops it, so every successful mDNS
maintenance query leaks one reference to the discovered service.
The leak alone would be benign, but the service owns the one-shot event
source that drives its maintenance schedule, and it only borrows a plain
pointer to its service browser. Once the extra reference is outstanding,
dns_service_browser_free() -> dns_remove_service() only drops the list's
reference, so dns_service_free() never runs, the maintenance timer is
never disabled, and the service outlives the browser it points at. The
next time the timer fires, mdns_maintenance_query() dereferences the
freed DnsServiceBrowser to reach the manager's event loop and to issue
the query.
Reaching it only takes a browse subscription whose record lives long
enough for one maintenance query to be answered, followed by the
subscription going away.
Make the reference a _cleanup_ one, the way the sibling
mdns_browse_service_query_complete() below already handles the reference
it takes on the service browser.
manpages build fails on Arch with new lxml:
Traceback (most recent call last):
File "/work/src/tools/make-directive-index.py", line 169, in make_page
_extract_directives(directive_groups, formatting, page)
~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/work/src/tools/make-directive-index.py", line 18, in _extract_directives
t = xml_parse(page)
File "/work/src/tools/xml_helper.py", line 25, in xml_parse
doc = tree.parse(page, _parser)
File "src/lxml/etree.pyx", line 3717, in lxml.etree.parse
File "src/lxml/parser.pxi", line 2045, in lxml.etree._parseDocument
File "src/lxml/parser.pxi", line 2071, in lxml.etree._parseDocumentFromURL
File "src/lxml/parser.pxi", line 1962, in lxml.etree._parseDocFromFile
File "src/lxml/parser.pxi", line 1231, in lxml.etree._BaseParser._parseDocFromFile
File "src/lxml/parser.pxi", line 647, in lxml.etree._ParserContext._handleParseResultDoc
File "src/lxml/parser.pxi", line 765, in lxml.etree._handleParseResult
File "src/lxml/parser.pxi", line 689, in lxml.etree._raiseParseError
File "../src/man/hostname.xml", line 5
lxml.etree.XMLSyntaxError: Entity 'entities' not defined, line 5, column 11
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/work/src/tools/make-directive-index.py", line 180, in <module>
_xml = make_page(_template_path, _xml_files)
File "/work/src/tools/make-directive-index.py", line 171, in make_page
raise ValueError('failed to process ' + page) from e
ValueError: failed to process ../src/man/hostname.xml
Relying on XMLParser() defaults leaves the external parameter entity
used to load custom-entities.ent unresolved. Request entity resolution explicitly.
Follow-up for 1a13e31d27
CPU architecture is determined by uname(), rather than the compile time
macros like __i386__, __x86_64__, __aarch64__.
Let's conditionalize with uname_architecture().
Follow-up for d518675741.
Since the D-Bus DNS-SD API was introduced in c3036641f0ed (v236),
`RegisterService()` has created an `sd_bus_track` object to destroy the
registered service when the registering client disconnects from the bus.
However, the track object is a `_cleanup_(sd_bus_track_unrefp)` local in
`bus_method_register_service()` that is never stored anywhere, so it is
destroyed again as soon as the method handler returns. The vanish
handler (`dnssd_registered_service_on_bus_track()`) has thus been dead
code for the past ~8 years.
The observable effect: services registered via D-Bus silently persist
after the client disconnects — unowned, advertised until resolved exits,
and impossible to re-register since the identifier stays taken
(`DNSSD_SERVICE_EXISTS`). At the same time, the registration doesn't
survive a resolved restart, so the current behavior is neither reliably
ephemeral nor reliably persistent. Noticed while reviewing a project
that (unknowingly relying on this bug) registers a service with a
one-shot `gdbus call` and expects it to persist.
This PR:
1. splits the goodbye/zone-removal logic out of
`bus_dnssd_method_unregister()` into a helper — the vanish path needs
the full teardown, not just `dnssd_registered_service_free()`, which
would leave the service's RRs published in the per-link mDNS zones;
2. stores the track object in `DnssdRegisteredService`, making client
tracking work as originally intended;
3. documents the lifetime semantics in `org.freedesktop.resolve1(5)`
(matching the equivalent Avahi D-Bus API, whose entry groups are
likewise bound to the client connection) and points to `*.dnssd` files
for registrations independent of a client's lifetime;
4. adds a TEST-75 case covering the connection-bound lifetime.
Note this is a behavioral change for fire-and-forget clients (one-shot
`busctl`/`gdbus` invocations). Arguably those were never working
reliably (registrations silently vanished on any resolved restart, and
stale entries blocked re-registration), but if this is deemed too risky,
the alternative would be to drop the dead tracking code and codify the
persist-until-exit behavior instead — I'd argue tracking as originally
intended is the better contract.
Verification: code compiles; the new integration test is included.
* 60fbc2d955 Update systemd to version 261.2 / rev 473 via SR 1368021
* c32d9727e6 Update systemd to version 261.1 / rev 472 via SR 1365923
* 74fd672013 Update systemd to version 260.3 / rev 471 via SR 1362701
* 769e1c4254 Update systemd to version 260.2 / rev 470 via SR 1361320
SUSE regularly builds packages into the future to track issues like the the
ones that might happen in 2038:
https://www.reddit.com/r/linux/comments/xjtf3q/in_the_year_2038/
Make sure that os_release_support_ended() doesn't fail in this case.
PS: on 32-bit systems time_t cannot represent dates past 2038, so use a safe
date there instead.
shellcheck 0.10+ reports SC2329 ("This function is never invoked") for
the per-testcase cleanup() functions in TEST-75-RESOLVED.sh, which are
only invoked indirectly via trap. The existing SC2317 markers don't
cover it, and inserting a new testcase shuffles which definitions the
heuristic fires on, making pre-existing findings show up as new in
differential CI runs. Extend the markers to SC2317,SC2329, matching
what testcase_dot_strict_per_link_verify() in this file and other test
units already do.
Verify that a DNS-SD service registered via the D-Bus RegisterService()
call is bound to the lifetime of the client's bus connection: since
busctl disconnects right after the call returns, the service must be
unregistered again shortly after. Wait for resolved's vanish handler
log message as proof that the client tracking triggered the
unregistration, check that a subsequent UnregisterService() fails
specifically because the service is not known anymore, and verify that
the identifier can be registered again — previously it stayed taken by
the vanished client's service forever, failing with
DNSSD_SERVICE_EXISTS.
A dummy link with mDNS enabled makes sure the per-scope goodbye/zone
teardown path actually runs, and since resolved would be restarted
automatically if it crashed in it, letting the D-Bus level checks pass
vacuously, verify the InvocationID stayed the same throughout.
Since the D-Bus DNS-SD API was introduced in c3036641f0 ("resolved:
implement D-Bus API for DNS-SD", v236), RegisterService() has set up an
sd_bus_track object so that a registered service is destroyed when the
client that registered it disconnects from the bus — there's even a
handler logging "Client of active request vanished, destroying DNS-SD
service."
However, the track object was never stored anywhere: it is declared as
a _cleanup_(sd_bus_track_unrefp) local in bus_method_register_service()
and hence destroyed again when the method handler returns, taking the
tracking down with it. The vanish handler was thus dead code, and
services registered via D-Bus silently persisted after their client
disconnected — unowned, impossible to re-register (the identifier
stays taken, failing with DNSSD_SERVICE_EXISTS), and advertised until
systemd-resolved itself exited.
Store the track object in DnssdRegisteredService, pinning it for the
lifetime of the service, so that client tracking works as originally
intended. Route the vanish path through the full unregistration logic
(goodbye packets + removal from the per-link mDNS zones), since freeing
the service alone would leave its RRs published in the zones.
Note for fire-and-forget clients (e.g. one-shot busctl/gdbus calls):
these were never reliable — the registration silently vanished on any
resolved restart — and now behave as documented for the corresponding
Avahi API as well: the bus connection must be kept open for as long as
the service is to remain registered. Document that in the man page and
NEWS, and point to *.dnssd files for registrations independent of a
client's lifetime.
dns_scope_announce(scope, /* goodbye= */ true) is not scoped to a
specific service: it walks the whole per-link mDNS zone and flags every
established RR as goodbye (TTL 0). Unregistering a single DNS-SD
service via the UnregisterService() D-Bus call hence withdrew the
host's own A/AAAA records and every other published service on the
link from remote caches, with nothing re-announcing them afterwards.
Introduce dns_scope_send_goodbye(), which sends an unsolicited response
for a caller-provided RR set, and let the DNS-SD unregistration path
send goodbye only for the RRs of the service actually being removed,
mirroring the flag logic of dns_scope_announce().
The goodbye answer is built per scope, and only includes records
actually established in the respective zone (or under re-verification,
in which case they are still served from it): a per-service goodbye
must take care not to withdraw records that are still being probed for
uniqueness, were withdrawn again after a conflict (in which case the
name belongs to the neighbor), or were never published in the scope in
the first place (e.g. on MulticastDNS=resolve links, which have an mDNS
scope but do not publish DNS-SD services).
This becomes more important with the next commit, which promotes
unregistration from an explicit method call to an automatic consequence
of the registering client disconnecting from the bus.
systemd-importd runs qcow2_detect()/qcow2_convert() over images it has just
downloaded (see pull-raw.c), so the header, the L1/L2 tables and every offset
and cluster descriptor in them are attacker controlled. There is a manual test
for the happy path, but nothing that feeds it a malformed image.
The corpus is two images, a plain data cluster and a zlib-compressed one (i.e.
decompress_zlib_raw()), at the 512 byte minimum cluster size and trimmed to the
last byte the converter reads, so 2K each. A zero cluster and a QCOW3 header are
a flipped descriptor bit and a bumped version field away from these, so they are
left to the fuzzer rather than carried in the tree.
fuzz-compress only ever feeds the decompressors their own output: it compresses
the fuzzer input and then decompresses the result, so the stream the decoders
see is well formed by construction. The blobs they get in reality come out of
journal files, systemd-journal-remote streams, coredumps and downloaded images,
i.e. from wherever the data was written, which is not necessarily somewhere we
trust.
So hand them the fuzzer input directly instead, across all four entrypoints:
decompress_blob(), decompress_startswith(), decompress_stream(), and the
decompressor_detect()/decompressor_push() pair that systemd-importd feeds from
the network. The latter is pushed in chunks, so that the decoders see the same
fragmentation a real download produces. The decompressed size is capped, as an
unbounded decode of a hostile blob measures the allocator rather than the
decoder.
The corpus carries the two truncated stream headers that used to send
decompress_startswith() allocating until OOM, fixed in the previous commit.
decompress_startswith() decodes just enough of a blob to compare its leading
bytes against a prefix, and grows the output buffer whenever a round of
decoding came back with less than prefix_len + 1 bytes decoded. That growth is
only the right answer while the decoder actually wants more output room. If it
instead ran out of input, i.e. the stream is truncated, then no amount of extra
room will help, and the gzip and bzip2 paths double their allocation on every
iteration until the process is out of memory.
Neither decoder lets us tell the two cases apart by return code alone:
inflate() reports the stalled stream as Z_BUF_ERROR, which we cannot simply
refuse since that is also how it asks for a bigger output buffer, and
BZ2_bzDecompress() does not distinguish them at all, it just keeps saying
BZ_OK. The output space left over does tell us, though: a decoder that stopped
without filling the buffer we gave it is waiting for input that is not coming,
so report the stream as corrupt instead of growing.
A 10 byte gzip header, or a 4 byte bzip2 header, with nothing behind it was
enough to reach a single 4 GiB allocation this way. The xz and zstd paths
already terminate, the former because liblzma turns a stalled stream into
LZMA_BUF_ERROR.
Note that no caller can reach this today: the only non-test caller is
decompress_startswith_journal(), and compression_supported_journal() admits
only NONE/XZ/LZ4/ZSTD. It is a trap for whoever wires up the other two, so
let's close it now.