Skip to content

Panic in write_utf8 (assert s.len() < 64) kills mDNS_daemon thread permanently on a ≥64-byte DNS label from the LAN #483

Description

@Gomouu

Summary

The mDNS_daemon thread panics via a hard assert! in DnsOutPacket::write_utf8 when it has to write a DNS label of 64 bytes or more:

// src/dns_parser.rs (v0.20.2, also present on `main`)
fn write_utf8(&mut self, s: &str) {
    assert!(s.len() < 64);          // <-- panics here
    self.write_byte(s.len() as u8);
    self.write_bytes(s.as_bytes());
}

Because this runs on the daemon's own background thread (mDNS_daemon), the panic is uncatchable by the library user: the thread unwinds, its flume senders are dropped, every browse/monitor Receiver disconnects, and mDNS discovery is silently and permanently dead for the rest of the process lifetime.

Observed in production on 2026-07-19 on a normal home LAN, apparently triggered by a single device emitting a malformed/oversized name.

thread 'mDNS_daemon' panicked at .../mdns-sd-0.20.2/src/dns_parser.rs:1623:
assertion failed: s.len() < 64

Why it is reachable from the network

write_utf8 is called by write_name for every label of every name written into an outgoing packet:

  • DnsOutPacket::write_questionwrite_name(&question.entry.name)
  • DnsOutPacket::write_recordwrite_name(record.get_name())
  • DnsPointer::writewrite_name(&self.alias) (PTR target)
  • DnsSrv::writewrite_name(&self.host) (SRV host)

Some of those names originate from records the daemon learned from the network and later re-emits (responses, known-answer suppression, re-announcements). So a remote peer on the LAN can get a name with a ≥ 64-byte label into the daemon's write path and take the whole daemon down. A single misbehaving device is enough.

Per RFC 1035 §2.3.4 / §3.1 a label is limited to 63 octets, so a ≥ 64-byte label is malformed input — but malformed input from the network should never be able to panic the daemon thread.

Expected behavior

The write path should treat an over-length label as a recoverable error rather than asserting:

  • make write_name / write_utf8 fallible (return an error) and have write_record / write_question drop or skip the offending record, or
  • defensively truncate/skip labels ≥ 64 bytes,

so that one malformed name cannot kill the mDNS_daemon thread and disable discovery process-wide.

Notes / transparency

  • Reproduced by reading the source; I do not have a captured packet of the exact offending name (it was seen once on a live network), so I can't yet point at the precise upstream that produced the ≥ 64-byte label. Happy to add a tcpdump/pcap capture if you can suggest which record type to watch for.
  • Downstream we are mitigating by supervising the ServiceDaemon: we detect the disconnected browse receiver as "daemon died" and recreate the daemon with exponential backoff. That keeps discovery alive, but the panic still costs a full daemon restart and a discovery gap, so a graceful fix in the writer would be much better.

Version: mdns-sd 0.20.2 (assertion also present on main).
Thanks for the library!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions