<feed xmlns='http://www.w3.org/2005/Atom'>
<title>odhcpd, branch master</title>
<subtitle>OpenWrt DHCP Server</subtitle>
<id>https://git.openwrt.org/project/odhcpd/atom?h=master</id>
<link rel='self' href='https://git.openwrt.org/project/odhcpd/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/odhcpd/'/>
<updated>2026-06-27T22:45:03Z</updated>
<entry>
<title>statefiles: escape client hostnames in the lease state file</title>
<updated>2026-06-27T22:45:03Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-06-27T20:00:36Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/odhcpd/commit/?id=68f382690bfaec56d5b1f31c3c31c48bcb642e3a'/>
<id>urn:sha1:68f382690bfaec56d5b1f31c3c31c48bcb642e3a</id>
<content type='text'>
A DHCP client fully controls its requested hostname (DHCPv4 option 12,
DHCPv6 FQDN option 39). The hostname is decoded with dn_expand(), and
since DNS labels may carry any octet (RFC 2181 section 11) while a valid
hostname is restricted to the LDH set (RFC 1035 section 2.3.1), the
decoded label can contain arbitrary bytes - spaces, control characters
and newlines included.

statefiles_write_state6()/statefiles_write_state4() wrote lease-&gt;hostname
into the space-delimited, line-based state file via fprintf("%s") without
escaping, regardless of lease-&gt;hostname_valid; the "broken\x20" marker was
only cosmetic. A newline in the hostname therefore injected an additional
'#'-prefixed line into the state file, forging an arbitrary extra lease
record, and a space forged additional fields on the same line. The forged
record is served verbatim by downstream consumers such as LuCI's
rpcd-mod-luci getDHCPLeases, turning an unauthenticated, pre-association
DHCPv6/DHCPv4 client into a lease-record spoofer and - combined with a
missing HTML-escape in LuCI - a stored XSS in the admin status page.

The dnsmasq hosts-file writers already bail on !hostname_valid, so only
the state file was affected.

Escape every byte that is not a bare LDH character as \xNN before writing
the hostname field. Valid hostnames are pure LDH and are emitted
unchanged, so only hostnames already flagged as broken are altered.

Reported-by: puru1761 (GHSA-hhmc-92hw-535f)
Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/odhcpd/pull/404
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>dhcpv6-ia: fix Reconfigure Accept Auth option write offset</title>
<updated>2026-06-17T23:04:19Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-06-17T22:57:46Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/odhcpd/commit/?id=03dacc23356b5789842b9d1bba0200fd005e35f3'/>
<id>urn:sha1:03dacc23356b5789842b9d1bba0200fd005e35f3</id>
<content type='text'>
Commit 0320032ae313 hoisted handshake_len above the Reconfigure Accept
block and pre-added sizeof(struct dhcpv6_auth_reconfigure) so the new
"buflen &gt;= handshake_len" guard would reserve the full 36-byte handshake.
It left the inner write as "memcpy(buf + handshake_len, ...)" and kept
the now-redundant "handshake_len += sizeof(auth)", so handshake_len was
36 (not 4) at the copy and ended at 68.

As a result the 32-byte AUTH option was written at buf + 36 instead of
buf + 4. The guard only ensures buflen &gt;= 36, so for buflen in [36, 67]
the copy ran past the buffer end - a reachable stack out-of-bounds write
(a REQUEST with 21 failing IA_PD options leaves buflen == 50 and
overflows the 512-byte pdbuf by 18 bytes). Even when the copy fit,
buf[4..35] was left uninitialised but counted in the response, leaking
32 bytes of stack and producing a malformed option stream.

Write the AUTH option at buf + 4 and drop the double-count so
handshake_len stays the validated full length. The "handshake_len &gt;
buflen" cap is now unreachable (handshake_len no longer changes inside
the block) and is removed.

Fixes: 0320032ae313 ("odhcpd: fix out of bounds write in dhcpv6_ia_handle_IAs")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>dhcpv6-ia: avoid dangling first lease pointer</title>
<updated>2026-06-17T19:26:53Z</updated>
<author>
<name>Caesurus</name>
</author>
<published>2026-05-14T20:42:26Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/odhcpd/commit/?id=26b122007030ebf192376aff8f98d428b25e2a93'/>
<id>urn:sha1:26b122007030ebf192376aff8f98d428b25e2a93</id>
<content type='text'>
odhcpd's DHCPv6 IA handler keeps a per-message `first`
lease pointer so multiple IA options in one message reuse
the same Reconfigure authentication key.

A DHCPv6 REQUEST from an adjacent-network client can make the
first IA set `first`, then make a later same-IAID IA fail the
on-link check and free that same lease, leaving `first` dangling.

A following IA in the same packet allocates a new lease and copies
`first-&gt;key`, producing a 16-byte heap-use-after-free read.
ASan confirms the UAF; native tracing shows the packet shape can
deterministically reclaim the freed lease as the destination
allocation, producing a zeroed Reconfigure key copy.

The validated impact is unauthenticated adjacent-network denial of
service and DHCPv6 Reconfigure/ForceRenew key confusion.
Arbitrary PC control is not proven for this UAF alone.

Fixes: CVE-2026-53918
Reported-by: OpenAI Security Team &lt;OutboundDisclosures@openai.com&gt;
Signed-off-by: David Bauer &lt;mail@david-bauer.net&gt;
</content>
</entry>
<entry>
<title>odhcpd: fix out of bounds write in dhcpv6_ia_handle_IAs</title>
<updated>2026-06-17T19:24:53Z</updated>
<author>
<name>David Bauer</name>
</author>
<published>2026-06-09T18:29:28Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/odhcpd/commit/?id=0320032ae313452f3c8cb8725c621dd06c36dc03'/>
<id>urn:sha1:0320032ae313452f3c8cb8725c621dd06c36dc03</id>
<content type='text'>
Check the buffer size before writing the DHCOv6 Reconf accept element to
the buffer. This was previously not the case, allowing an out of bounds
write.

Fixes: CVE-2026-53921
Reported-by: Ying Dong &lt;dyingc@gmail.com&gt;
Signed-off-by: David Bauer &lt;mail@david-bauer.net&gt;
</content>
</entry>
<entry>
<title>odhcpd: fix integer underflow in dhcpv6_ia_handle_IAs</title>
<updated>2026-06-14T21:19:22Z</updated>
<author>
<name>David Bauer</name>
</author>
<published>2026-06-09T19:42:17Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/odhcpd/commit/?id=c6792bac3905d4bf726d914e9994f6d4b98c5b57'/>
<id>urn:sha1:c6792bac3905d4bf726d914e9994f6d4b98c5b57</id>
<content type='text'>
With buflen underflowed to ~SIZE_MAX, every subsequent check inside
build_ia passes, and the next matched IA serializes its IA_ADDR sub-options
into the caller's pdbuf[512] with no effective bound, smashing the
parent stack frame.

Cap the subtraction to avoid underflowing buflen.

Fixes: CVE-2026-53922
Reported-by: Trevor Lawrence &lt;_SiCk@afflicted.sh&gt;
Signed-off-by: David Bauer &lt;mail@david-bauer.net&gt;
</content>
</entry>
<entry>
<title>odhcpd: fix out-of-bounds write in build_ia</title>
<updated>2026-06-14T21:19:22Z</updated>
<author>
<name>David Bauer</name>
</author>
<published>2026-06-09T18:16:40Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/odhcpd/commit/?id=d329a15413387027453644cd363dca06ade28328'/>
<id>urn:sha1:d329a15413387027453644cd363dca06ade28328</id>
<content type='text'>
If a error status shall be indicated, odhcpd did not validate the size
of the available buffer correctly.

Account for the additionally required space and exit early in case the
available buffer is not sufficient.

Fixes: CVE-2026-53921
Fixes: CVE-2026-53922
Reported-by: Ying Dong &lt;dyingc@gmail.com&gt;
Reported-by: Trevor Lawrence &lt;_SiCk@afflicted.sh&gt;
Signed-off-by: David Bauer &lt;mail@david-bauer.net&gt;
</content>
</entry>
<entry>
<title>dhcpv6-ia: signal buffer-full from build_ia() IA_ADDR branch</title>
<updated>2026-06-03T23:05:46Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-30T22:25:16Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/odhcpd/commit/?id=1782f3f2aad21cd7971798c898355a12a9611304'/>
<id>urn:sha1:1782f3f2aad21cd7971798c898355a12a9611304</id>
<content type='text'>
In the CONFIRM/RELEASE/DECLINE echo path of build_ia(), the IA_PREFIX branch
returns 0 (the buffer-full failure signal) when the next IA_PREFIX option will
not fit, but the sibling IA_ADDR branch used "continue" instead. That kept the
option loop running and then fell through to writing the IA header length and
returning a non-zero ia_len, so the caller treated the truncated IA -- now
missing the IA_ADDR(s) that did not fit -- as a successfully built option and
put it on the wire. Return 0 from the IA_ADDR branch as well so a full buffer
is reported consistently and a malformed IA is never emitted.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/odhcpd/pull/401
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>dhcpv6: validate rewritten RELAY_MSG length in update_nested_message()</title>
<updated>2026-06-03T23:05:41Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-30T22:12:10Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/odhcpd/commit/?id=5b1e3befb0b23f7b7506fb6209aed3aa9e04a6f7'/>
<id>urn:sha1:5b1e3befb0b23f7b7506fb6209aed3aa9e04a6f7</id>
<content type='text'>
When building a relay-reply, update_nested_message() walks the nested
RELAY-FORW envelope of the (untrusted) request and adjusts every
OPTION_RELAY_MSG length by pdiff, the signed difference between the assembled
reply options and the original request options. It did this with

	olen += pdiff;                                  /* olen is uint16_t */
	odata[-2] = olen &gt;&gt; 8; odata[-1] = olen;
	update_nested_message(odata, olen - pdiff, ...);

If "olen + pdiff" leaves the 0..65535 range, the uint16_t wraps: the wrapped
value is written into the on-wire RELAY_MSG length field, and "olen - pdiff"
no longer recovers the original (iterator-validated) inner length. The bogus
length is then passed as the recursion's buffer size, so the inner option
walk uses "odata + bogus_len" as its end bound and reads option headers far
past the end of the receive buffer (an out-of-bounds read, plus a 2-byte
out-of-bounds write if a RELAY_MSG pattern is hit) driven by a crafted
multi-level Relay-Forward packet.

Compute the new length in a signed wide type, bail out if it would not fit in
the 16-bit field, and pass the already-validated original olen to the
recursion instead of reconstructing it from the mutated value.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/odhcpd/pull/401
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>dhcpv4: pass &amp;dest.sin_addr to inet_ntop() when logging</title>
<updated>2026-06-03T23:05:36Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-30T22:12:08Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/odhcpd/commit/?id=c2f94ee5c8e7bc0ed60a0895e717e1fe09799bb0'/>
<id>urn:sha1:c2f94ee5c8e7bc0ed60a0895e717e1fe09799bb0</id>
<content type='text'>
dhcpv4_fr_send() passed &amp;dest (a struct sockaddr_in) to
inet_ntop(AF_INET, ...), which expects a pointer to a 4-byte struct in_addr.
inet_ntop therefore formatted the first four bytes of the sockaddr
(sin_family and half of sin_port) instead of the target IPv4 address, so the
FORCERENEW send / send-failure log lines printed a meaningless address. Point
inet_ntop at dest.sin_addr, like the correct call site elsewhere in the file.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/odhcpd/pull/401
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>router: keep RA DNS option lengths within the uint8 length field</title>
<updated>2026-06-03T23:05:32Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-30T21:43:36Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/odhcpd/commit/?id=60f488192d5d32023455d51e37ea9f11819e417a'/>
<id>urn:sha1:60f488192d5d32023455d51e37ea9f11819e417a</id>
<content type='text'>
The RDNSS and DNSSL option length fields (RFC 8106) are uint8_t values
counting 8-byte units, but send_router_advert() computed them from the
configured DNS server count / search-list size without any bound. With 128 or
more configured DNS servers, "1 + 2*dns_addrs6_cnt" exceeds 255 and the len
field wrapped; likewise a search list larger than UINT8_MAX*8 bytes wrapped
search_sz/8. In both cases the actual bytes written (sized via dns_sz /
search_sz and the iovec length) used the full untruncated size, so the
emitted option's Length no longer matched its real size and clients would
mis-parse or drop the whole RA.

Cap the RDNSS server count at 127 (1 + 2*127 == 255) and skip the DNSSL
option entirely when it would not fit in a valid uint8 length, matching the
existing UINT8_MAX guard on the DHCPv4 search-list path.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/odhcpd/pull/401
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
</feed>
