]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
dhcp-client: clear overloaded sname/file fields after parsing
authorLuca Boccassi <luca.boccassi@gmail.com>
Sun, 17 May 2026 17:22:20 +0000 (18:22 +0100)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 17 May 2026 19:52:25 +0000 (04:52 +0900)
When SD_DHCP_OPTION_OVERLOAD indicates that the sname and/or file header
fields are overloaded with extra DHCP options, dhcp_message_parse() merges
those options into message->options but leaves the raw bytes untouched in
the header. As a result, dhcp_message_build() emits the header (including
the overloaded bytes) verbatim, and the next parse re-parses those bytes,
appending duplicate entries to the options map (each tag's iov list grows).
Subsequent builds then differ from the first, breaking the parse/build
roundtrip.

This was caught by fuzz-dhcp-client, which asserts that two consecutive
build calls produce identical output.

Zero out the overloaded fields after parsing them, since their content has
already been merged into the options map. This makes the roundtrip
idempotent and avoids re-emitting stale overloaded data in the rebuilt
header. The JSON build/parse path was already correct (it omits sname/file
from the JSON when the overload bit is set), so only the binary path needed
fixing.

Fixes https://github.com/systemd/systemd/issues/42139

Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
src/libsystemd-network/dhcp-message.c
test/fuzz/fuzz-dhcp-client/overload-file-roundtrip [new file with mode: 0644]

index ae12c5b46a97646458d4f8125fdd1954d3f4b8ed..a3ba05688df073c21c67ecca1e8c7853b91eac4b 100644 (file)
@@ -1409,12 +1409,19 @@ int dhcp_message_parse(
                 r = tlv_parse(&message->options, &IOVEC_MAKE(message->header.file, sizeof(message->header.file)));
                 if (r < 0)
                         return r;
+
+                /* The content of the overloaded field has been merged into options. Clear it so that the
+                 * field is not re-parsed (which would duplicate the options) and not re-emitted verbatim
+                 * by dhcp_message_build(), ensuring parse/build is idempotent. */
+                memzero(message->header.file, sizeof(message->header.file));
         }
 
         if (FLAGS_SET(overload, DHCP_OVERLOAD_SNAME)) {
                 r = tlv_parse(&message->options, &IOVEC_MAKE(message->header.sname, sizeof(message->header.sname)));
                 if (r < 0)
                         return r;
+
+                memzero(message->header.sname, sizeof(message->header.sname));
         }
 
         *ret = TAKE_PTR(message);
diff --git a/test/fuzz/fuzz-dhcp-client/overload-file-roundtrip b/test/fuzz/fuzz-dhcp-client/overload-file-roundtrip
new file mode 100644 (file)
index 0000000..43cee11
Binary files /dev/null and b/test/fuzz/fuzz-dhcp-client/overload-file-roundtrip differ