Hello, The attached patch fixes a memory leak related to parsing configurable SSL error details.
Trunk r11496 "Configurable SSL error details messages" correctly disabled collection of HTTP statistics for non-HTTP header fields, such as configurable SSL error details. However, it also incorrectly disabled deletion of those non-HTTP header fields. Configurable SSL error details are only created during [re]configuration time, so the leak went unnoticed since 2011-06-17. However, when annotations were added to non-HTTP header fields (r12413), the old bug resulted in a major leak for configurations that annotate transactions. Fortunately, that major leak was removed by r12779 "helperNotes to NotePairs" polishing, which stopped using HttpHeader for annotations. However, any code based on trunk between r12413 and r12779 needs this fix (or needs to be resynced with the current trunk). HTH, Alex.
Bug: Leaking configurable SSL error details. Trunk r11496 "Configurable SSL error details messages" correctly disabled collection of HTTP statistics for non-HTTP header fields, such as configurable SSL error details. However, it also incorrectly disabled deletion of those non-HTTP header fields. Configurable SSL error details are only created during [re]configuration time, so the leak went unnoticed since 2011-06-17. However, when annotations were added to non-HTTP header fields (r12413), the old bug resulted in a major leak for configurations that annotate transactions. Fortunately, that major leak was removed by r12779 "helperNotes to NotePairs" polishing, which stopped using HttpHeader for annotations. === modified file 'src/HttpHeader.cc' --- src/HttpHeader.cc 2012-11-26 18:54:43 +0000 +++ src/HttpHeader.cc 2013-05-10 21:50:56 +0000 @@ -421,71 +421,71 @@ // we do not really care, but the caller probably does assert(owner == other.owner); clean(); update(&other, NULL); // will update the mask as well len = other.len; } return *this; } void HttpHeader::clean() { HttpHeaderPos pos = HttpHeaderInitPos; HttpHeaderEntry *e; assert(owner > hoNone && owner < hoEnd); debugs(55, 7, "cleaning hdr: " << this << " owner: " << owner); PROF_start(HttpHeaderClean); + if (owner <= hoReply) { /* * An unfortunate bug. The entries array is initialized * such that count is set to zero. httpHeaderClean() seems to * be called both when 'hdr' is created, and destroyed. Thus, * we accumulate a large number of zero counts for 'hdr' before * it is ever used. Can't think of a good way to fix it, except * adding a state variable that indicates whether or not 'hdr' * has been used. As a hack, just never count zero-sized header * arrays. */ - - if (owner <= hoReply) { if (0 != entries.count) HttpHeaderStats[owner].hdrUCountDistr.count(entries.count); ++ HttpHeaderStats[owner].destroyedCount; HttpHeaderStats[owner].busyDestroyedCount += entries.count > 0; + } // if (owner <= hoReply) while ((e = getEntry(&pos))) { /* tmp hack to try to avoid coredumps */ if (e->id < 0 || e->id >= HDR_ENUM_END) { debugs(55, DBG_CRITICAL, "HttpHeader::clean BUG: entry[" << pos << "] is invalid (" << e->id << "). Ignored."); } else { + if (owner <= hoReply) HttpHeaderStats[owner].fieldTypeDistr.count(e->id); /* yes, this deletion leaves us in an inconsistent state */ delete e; } } - } // if (owner <= hoReply) entries.clean(); httpHeaderMaskInit(&mask, 0); len = 0; PROF_stop(HttpHeaderClean); } /* append entries (also see httpHeaderUpdate) */ void HttpHeader::append(const HttpHeader * src) { const HttpHeaderEntry *e; HttpHeaderPos pos = HttpHeaderInitPos; assert(src); assert(src != this); debugs(55, 7, "appending hdr: " << this << " += " << src); while ((e = src->getEntry(&pos))) { addEntry(e->clone()); } }