GH-532: improve disable_analyzer BIF

- Add an extra "prevent" parameter (default value of false), which
  helps prevent the same analyzer type from being attached in the
  future.  It's useful in situations where you want to disable early
  on, but a DPD signature may still trigger later and re-attach
  the same analyzer.  E.g. when not using this flag, but calling
  disable_analyzer() inside an http_request event, will remove the
  HTTP analyzer that was attached due to well-known-port, but a later
  DPD signature match from upon seeing the HTTP reply will end up
  attaching another HTTP analyzer.  More surprising is that upon
  re-attaching that analyzer, you'll get the same http_request as
  before since the DPD buffer will get replayed into the new analyzer.

- Fixes disable_analyzer() to work when called even earlier, like
  within the protocol_confirmation event.  At that time, the
  Analyzer tree may have not properly added the new analyzer into
  Analyzer::children yet, but rather the temporary waiting list,
  Analyzer::new_children.  Analyzer::RemoveChildAnalyzer previously
  did not inspect the later list.

- Fixes disable_analyzer() when called on an analyzer added to the
  tree via TCP_Analyzer::AddChildPacketAnalyzer.  TCP_Analyzer
  keeps track of such children in its own list,
  TCP_Analyzer::packet_children, which the previous
  Analyzer::RemoveChildAnalyzer implementation didn't inspect.
This commit is contained in:
Jon Siwek 2019-08-09 19:51:08 -07:00
parent bf9b983f00
commit a1c5eddb95
12 changed files with 247 additions and 58 deletions

View file

@ -91,12 +91,6 @@ bool Analyzer::IsAnalyzer(const char* name)
return strcmp(analyzer_mgr->GetComponentName(tag).c_str(), name) == 0;
}
// Used in debugging output.
static string fmt_analyzer(Analyzer* a)
{
return string(a->GetAnalyzerName()) + fmt("[%d]", a->GetID());
}
Analyzer::Analyzer(const char* name, Connection* conn)
{
Tag tag = analyzer_mgr->GetComponentTag(name);
@ -381,7 +375,11 @@ void Analyzer::ForwardEndOfData(bool orig)
bool Analyzer::AddChildAnalyzer(Analyzer* analyzer, bool init)
{
if ( HasChildAnalyzer(analyzer->GetAnalyzerTag()) )
auto t = analyzer->GetAnalyzerTag();
auto it = std::find(prevented.begin(), prevented.end(), t);
auto prevent = it != prevented.end();
if ( HasChildAnalyzer(t) || prevent )
{
analyzer->Done();
delete analyzer;
@ -407,46 +405,67 @@ bool Analyzer::AddChildAnalyzer(Analyzer* analyzer, bool init)
Analyzer* Analyzer::AddChildAnalyzer(Tag analyzer)
{
if ( ! HasChildAnalyzer(analyzer) )
{
Analyzer* a = analyzer_mgr->InstantiateAnalyzer(analyzer, conn);
if ( HasChildAnalyzer(analyzer) )
return nullptr;
if ( a && AddChildAnalyzer(a) )
return a;
auto it = std::find(prevented.begin(), prevented.end(), analyzer);
if ( it != prevented.end() )
return nullptr;
Analyzer* a = analyzer_mgr->InstantiateAnalyzer(analyzer, conn);
if ( a && AddChildAnalyzer(a) )
return a;
return nullptr;
}
bool Analyzer::RemoveChild(const analyzer_list& children, ID id)
{
for ( const auto& i : children )
{
if ( i->id != id )
continue;
if ( i->finished || i->removing )
return false;
DBG_LOG(DBG_ANALYZER, "%s disabling child %s",
fmt_analyzer(this).c_str(), fmt_analyzer(i).c_str());
// We just flag it as being removed here but postpone
// actually doing that to later. Otherwise, we'd need
// to call Done() here, which then in turn might
// cause further code to be executed that may assume
// something not true because of a violation that
// triggered the removal in the first place.
i->removing = true;
return true;
}
return 0;
return false;
}
void Analyzer::RemoveChildAnalyzer(Analyzer* analyzer)
bool Analyzer::RemoveChildAnalyzer(ID id)
{
LOOP_OVER_CHILDREN(i)
if ( *i == analyzer && ! (analyzer->finished || analyzer->removing) )
{
DBG_LOG(DBG_ANALYZER, "%s disabling child %s",
fmt_analyzer(this).c_str(), fmt_analyzer(*i).c_str());
// We just flag it as being removed here but postpone
// actually doing that to later. Otherwise, we'd need
// to call Done() here, which then in turn might
// cause further code to be executed that may assume
// something not true because of a violation that
// triggered the removal in the first place.
(*i)->removing = true;
return;
}
return RemoveChild(children, id) || RemoveChild(new_children, id);
}
void Analyzer::RemoveChildAnalyzer(ID id)
bool Analyzer::Remove()
{
LOOP_OVER_CHILDREN(i)
if ( (*i)->id == id && ! ((*i)->finished || (*i)->removing) )
{
DBG_LOG(DBG_ANALYZER, "%s disabling child %s",
fmt_analyzer(this).c_str(), fmt_analyzer(*i).c_str());
// See comment above.
(*i)->removing = true;
return;
}
assert(parent);
parent->RemoveChildAnalyzer(this);
return removing;
}
void Analyzer::PreventChildren(Tag tag)
{
auto it = std::find(prevented.begin(), prevented.end(), tag);
if ( it != prevented.end() )
return;
prevented.emplace_back(tag);
}
bool Analyzer::HasChildAnalyzer(Tag tag)