LEAmDNS: _parseResponse keeps every answer record of a packet on the heap, so one large answer can abort with out of memory

Ouverte
#9,343 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Les mainteneurs répondent en général sous 1 jour

Personne n'a encore pris cette issue.

Évaluation

Difficulté
4/5
Temps estimé
3-5 jours
Accessibilité débutants
52/100
Type d'issue
Bug
Clarté
Plutôt claire
Activité
Active
Stack technique
arduino, cpp
Domaine
embedded-iot

Piste de recherche

Start in libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp: trace _parseResponse through _readRRAnswer and _processAnswers, then compare with _parseQuery's record handling. The report proposes processing and freeing each response record before reading the next; done means response handling no longer retains all packet records at once while preserving probe and service query behavior. The reporter says they have a patched copy, but provides no test or captured packet.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Description

What happens

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:

operator new (288 bytes) failed
  <- MDNSResponder::_readRRAnswer
  <- MDNSResponder::_parseResponse
  <- MDNSResponder::_parseMessage
  <- MDNSResponder::_process
free heap 952 bytes, largest free block 248 bytes

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.

Langage dominant
C++
Étoiles
16.7k
Forks
13k
Merge moyen
1 j 2 h
PR mergées (30 j)
7

Préparer son environnement

Ce projet ne fournit ni conteneur de développement, ni Dockerfile, ni guide de contribution : l'installation est à votre charge. Commencez par son README, et consultez notre guide de la première contribution pour les étapes générales.

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Autres issues de esp8266/Arduino

Toutes les issues de esp8266/Arduino

Issues similaires

Plus d'issues C++

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.