Any host on the LAN can send two mDNS records and make the responder write past the end of its
transmit packet.
The string table stores each name in a slot rounded up to a multiple of four:
```c
/* addons/mdns/nxd_mdns.c:11436, 11443, 11447 */
memory_len = ((memory_len & 0xFFFFFFFC) + 8) & 0xFFFFFFFF;
...
len = *((USHORT*)(p - 2)); /* slot size, not string length */
if ((len == memory_len) && ... _nx_mdns_name_match(start, memory_ptr, memory_size) ...)
```
The lookup that decides whether an incoming name is already stored compares the rounded slot size,
so names of 12, 13, 14 and 15 characters share one bucket. A second name in the bucket is answered
with the pointer to the first, and the record then carries a string up to three bytes longer than
the length the caller accounted for. `_nx_mdns_packet_rr_add` (nxd_mdns.c:8911) sizes its only
bound check from that stale length, and `_nx_mdns_name_string_encode` writes the real string.
Two PTR records are enough, both ordinary mDNS responses to a `_http._tcp` query, with owner names
whose lengths fall in the same bucket:
```
==87491==ERROR: AddressSanitizer: heap-buffer-overflow
WRITE of size 1 at 0x611000000124 thread T5
#0 _nx_mdns_name_string_encode addons/mdns/nxd_mdns.c:13096
#1 _nx_mdns_packet_rr_add addons/mdns/nxd_mdns.c:8911
0x611000000124 is 0 bytes to the right of 228-byte region
```
The overflow is one to three bytes of attacker-influenced name data past `nx_packet_data_end`. In a
normal pool that lands in the next packet in the same pool rather than in a redzone, so the visible
effect is a corrupted neighbouring packet or a corrupted pool free list rather than a clean crash.
Compare the slot size against the stored string length before declaring a match, or keep the
string length in the slot header and return it to the caller so the encoder and the bound check
agree.
GHSA-rp9p-ggxc-p9xm
Any host on the LAN can send two mDNS records and make the responder write past the end of its
...
https://github.com/advisories/GHSA-rp9p-ggxc-p9xmAny host on the LAN can send two mDNS records and make the responder write past the end of its
transmit packet.
The string table stores each name in a slot rounded up to a multiple of four:
/* addons/mdns/nxd_mdns.c:11436, 11443, 11447 */
memory_len = ((memory_len & 0xFFFFFFFC) + 8) & 0xFFFFFFFF;
...
len = *((USHORT*)(p - 2)); /* slot size, not string length */
if ((len == memory_len) && ... _nx_mdns_name_match(start, memory_ptr, memory_size) ...)
The lookup that decides whether an incoming name is already stored compares the rounded slot size,
so names of 12, 13, 14 and 15 characters share one bucket. A second name in the bucket is answered
with the pointer to the first, and the record then carries a string up to three bytes longer than
the length the caller accounted for. _nx_mdns_packet_rr_add (nxd_mdns.c:8911) sizes its only
bound check from that stale length, and _nx_mdns_name_string_encode writes the real string.
Two PTR records are enough, both ordinary mDNS responses to a _http._tcp query, with owner names
whose lengths fall in the same bucket:
==87491==ERROR: AddressSanitizer: heap-buffer-overflow
WRITE of size 1 at 0x611000000124 thread T5
#0 _nx_mdns_name_string_encode addons/mdns/nxd_mdns.c:13096
#1 _nx_mdns_packet_rr_add addons/mdns/nxd_mdns.c:8911
0x611000000124 is 0 bytes to the right of 228-byte region
The overflow is one to three bytes of attacker-influenced name data past nx_packet_data_end. In a
normal pool that lands in the next packet in the same pool rather than in a redzone, so the visible
effect is a corrupted neighbouring packet or a corrupted pool free list rather than a clean crash.
Compare the slot size against the stored string length before declaring a match, or keep the
string length in the slot header and return it to the caller so the encoder and the bound check
agree.
Click to expand
{
"dataType": "CVE_RECORD",
"dataVersion": "5.2",
"cveMetadata": {
"cveId": "CVE-2026-102715",
"assignerOrgId": "e51fbebd-6053-4e49-959f-1b94eeb69a2c",
"assignerShortName": "eclipse",
"dateUpdated": "2026-09-29T17:42:35.719Z",
"dateReserved": "2026-09-29T16:15:11.235Z",
"datePublished": "2026-09-29T17:42:35.719Z",
"state": "PUBLISHED"
},
"containers": {
"cna": {
"providerMetadata": {
"orgId": "e51fbebd-6053-4e49-959f-1b94eeb69a2c",
"shortName": "eclipse",
"dateUpdated": "2026-09-29T17:42:35.719Z"
},
"title": "mDNS string-cache lookup matches on slot size, so a peer name aliases a shorter one and the response encoder writes past the packet",
"descriptions": [
{
"lang": "en",
"value": "Any host on the LAN can send two mDNS records and make the responder write past the end of its\n\n\n\ntransmit packet.\n\n\n\nThe string table stores each name in a slot rounded up to a multiple of four:\n\n\n\n```c\n\n\n\n/* addons/mdns/nxd_mdns.c:11436, 11443, 11447 */\n\n\n\nmemory_len = ((memory_len & 0xFFFFFFFC) + 8) & 0xFFFFFFFF;\n\n\n\n...\n\n\n\nlen = *((USHORT*)(p - 2)); /* slot size, not string length */\n\n\n\nif ((len == memory_len) && ... _nx_mdns_name_match(start, memory_ptr, memory_size) ...)\n\n\n\n```\n\n\n\nThe lookup that decides whether an incoming name is already stored compares the rounded slot size,\n\n\n\nso names of 12, 13, 14 and 15 characters share one bucket. A second name in the bucket is answered\n\n\n\nwith the pointer to the first, and the record then carries a string up to three bytes longer than\n\n\n\nthe length the caller accounted for. `_nx_mdns_packet_rr_add` (nxd_mdns.c:8911) sizes its only\n\n\n\nbound check from that stale length, and `_nx_mdns_name_string_encode` writes the real string.\n\n\n\nTwo PTR records are enough, both ordinary mDNS responses to a `_http._tcp` query, with owner names\n\n\n\nwhose lengths fall in the same bucket:\n\n\n\n```\n\n\n\n==87491==ERROR: AddressSanitizer: heap-buffer-overflow\n\n\n\nWRITE of size 1 at 0x611000000124 thread T5\n\n #0 _nx_mdns_name_string_encode addons/mdns/nxd_mdns.c:13096\n #1 _nx_mdns_packet_rr_add addons/mdns/nxd_mdns.c:8911\n\n\n0x611000000124 is 0 bytes to the right of 228-byte region\n\n\n\n```\n\n\n\nThe overflow is one to three bytes of attacker-influenced name data past `nx_packet_data_end`. In a\n\n\n\nnormal pool that lands in the next packet in the same pool rather than in a redzone, so the visible\n\n\n\neffect is a corrupted neighbouring packet or a corrupted pool free list rather than a clean crash.\n\n\n\nCompare the slot size against the stored string length before declaring a match, or keep the\n\n\n\nstring length in the slot header and return it to the caller so the encoder and the bound check\n\n\n\nagree.",
"supportingMedia": [
{
"type": "text/html",
"base64": false,
"value": "<p>Any host on the LAN can send two mDNS records and make the responder write past the end of its</p><p>transmit packet.</p><p>The string table stores each name in a slot rounded up to a multiple of four:</p><p>```c</p><p>/* addons/mdns/nxd_mdns.c:11436, 11443, 11447 */</p><p>memory_len = ((memory_len & 0xFFFFFFFC) + 8) & 0xFFFFFFFF;</p><p>...</p><p>len = *((USHORT*)(p - 2)); /* slot size, not string length */</p><p>if ((len == memory_len) && ... _nx_mdns_name_match(start, memory_ptr, memory_size) ...)</p><p>```</p><p>The lookup that decides whether an incoming name is already stored compares the rounded slot size,</p><p>so names of 12, 13, 14 and 15 characters share one bucket. A second name in the bucket is answered</p><p>with the pointer to the first, and the record then carries a string up to three bytes longer than</p><p>the length the caller accounted for. `_nx_mdns_packet_rr_add` (nxd_mdns.c:8911) sizes its only</p><p>bound check from that stale length, and `_nx_mdns_name_string_encode` writes the real string.</p><p>Two PTR records are enough, both ordinary mDNS responses to a `_http._tcp` query, with owner names</p><p>whose lengths fall in the same bucket:</p><p>```</p><p>==87491==ERROR: AddressSanitizer: heap-buffer-overflow</p><p>WRITE of size 1 at 0x611000000124 thread T5</p><code> #0 _nx_mdns_name_string_encode addons/mdns/nxd_mdns.c:13096</code><br><code> #1 _nx_mdns_packet_rr_add addons/mdns/nxd_mdns.c:8911</code><br><p>0x611000000124 is 0 bytes to the right of 228-byte region</p><p>```</p><p>The overflow is one to three bytes of attacker-influenced name data past `nx_packet_data_end`. In a</p><p>normal pool that lands in the next packet in the same pool rather than in a redzone, so the visible</p><p>effect is a corrupted neighbouring packet or a corrupted pool free list rather than a clean crash.</p><p>Compare the slot size against the stored string length before declaring a match, or keep the</p><p>string length in the slot header and return it to the caller so the encoder and the bound check</p><p>agree.</p>"
}
]
}
],
"affected": [
{
"vendor": "Eclipse Foundation",
"product": "eclipse-threadx/netxduo",
"defaultStatus": "unaffected",
"versions": [
{
"version": "0",
"status": "affected",
"versionType": "semver",
"lessThanOrEqual": "6.5.1"
}
]
}
],
"problemTypes": [
{
"descriptions": [
{
"lang": "en",
"description": "CWE-787 Out-of-bounds Write",
"cweId": "CWE-787",
"type": "CWE"
}
]
}
],
"references": [
{
"url": "https://github.com/eclipse-threadx/netxduo/security/advisories/GHSA-2gf7-5224-5vrj"
}
],
"metrics": [
{
"format": "CVSS",
"scenarios": [
{
"lang": "en",
"value": "GENERAL"
}
]
}
],
"credits": [
{
"lang": "en",
"value": "L0stHeart",
"type": "reporter"
}
]
}
}
}