You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
LEAmDNS: _parseResponse keeps every answer record of a packet on the heap, so one large answer can abort with out of memory #9343
An ESP8266 (core 3.1.2, a GeekMagic SmallTV-Ultra with about 6 KB of free heap while running) restarted after
11.4 hours with an out-of-memory abort inside LEAmDNS. Our crash record, decoded with addr2line:
The lowest values recorded before, during those hours, were about 4.4 KB free and a 1.5 KB largest block. So
about 3.5 KB went within a moment. The sketch only advertises (MDNS.begin, addService, addServiceTxt, and MDNS.update() in loop()); it sends no queries.
Why, from reading the code (master, libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp)
_parseResponse reads answers only while a service query waits or a probe runs (line 742-743). Probing runs
after MDNS.begin and again after every interface status change (LEAmDNS.cpp, the statusChangeCB that
calls _restart()), for example a Wi-Fi reconnect.
In that state, every record of the incoming packet (ANCount + NSCount + ARCount, taken from the packet
header) is read with _readRRAnswer into its own heap object and kept in a list (lines 764-775).
The list goes to _processAnswers as a whole (line 794) and is freed only after that (lines 804-810).
So the heap needed grows with the number of records in someone else's answer. A phone, TV or speaker that
answers with many records during the probe window can take several KB at once. new aborts on failure in this
core, so the library's own checks for a failed allocation never run, and the result is a restart.
What we did not measure: we have not captured the packet, and we don't know for sure that a reconnect came just
before. That part is our reading of the code.
_parseQuery handles known answers differently: it frees each record right after reading it (so one record at a
time), which is why we think the same can work for responses.
Possible fix
Handle each answer record in _parseResponse as soon as it is read, and free it before reading the next, so the
peak is one record instead of the whole packet. That means splitting _processAnswers so it can take one record
at a time. A simpler, partial alternative: skip (read past without allocating) records whose names cannot
concern this responder.
We are trying the first approach in a patched copy for our own firmware and can open a PR if this direction is
welcome.
Activity