Fix reference counting issues related to lambdas/closures

For example, circular references between a lambda function the frame
it's stored within and/or its closure could cause memory leaks.

This also fixes other various reference-count ownership issues that
could lead to memory errors.

There may still be some potential/undiscovered issues because the "outer
ID" finding logic doesn't look quite right as the AST traversal descends
within nested lambdas and considers their locals as "outer", but
possibly the other logic for locating values in closures or cloning
closures just works around that behavior.
This commit is contained in:
Jon Siwek 2020-01-01 12:13:02 -08:00
parent 0fe2a14d98
commit 44d922c4b5
11 changed files with 283 additions and 78 deletions

View file

@ -31,20 +31,54 @@ Frame::Frame(int arg_size, const BroFunc* func, const val_list* fn_args)
Frame::~Frame()
{
for ( auto& func : functions_with_closure_frame_reference )
{
func->StrengthenClosureReference(this);
Unref(func);
}
// Deleting a Frame that is a view is a no-op.
Unref(trigger);
Unref(closure);
if ( ! weak_closure_ref )
Unref(closure);
for ( auto& i : outer_ids )
Unref(i);
Release();
delete [] weak_refs;
}
void Frame::SetElement(int n, Val* v)
void Frame::AddFunctionWithClosureRef(BroFunc* func)
{
Unref(frame[n]);
::Ref(func);
functions_with_closure_frame_reference.emplace_back(func);
}
void Frame::SetElement(int n, Val* v, bool weak_ref)
{
UnrefElement(n);
frame[n] = v;
if ( weak_ref )
{
if ( ! weak_refs )
{
weak_refs = new bool[size];
for ( auto i = 0; i < size; ++i )
weak_refs[i] = false;
}
weak_refs[n] = true;
}
else
{
if ( weak_refs )
weak_refs[n] = false;
}
}
void Frame::SetElement(const ID* id, Val* v)
@ -62,8 +96,15 @@ void Frame::SetElement(const ID* id, Val* v)
if ( offset_map.size() )
{
auto where = offset_map.find(std::string(id->Name()));
if ( where != offset_map.end() )
SetElement(where->second, v);
{
// Need to add a Ref to 'v' since the SetElement() for
// id->Offset() below is otherwise responsible for keeping track
// of the implied reference count of the passed-in 'v' argument.
// i.e. if we end up storing it twice, we need an addition Ref.
SetElement(where->second, v->Ref());
}
}
SetElement(id->Offset(), v);
@ -92,7 +133,7 @@ void Frame::Reset(int startIdx)
{
for ( int i = startIdx; i < size; ++i )
{
Unref(frame[i]);
UnrefElement(i);
frame[i] = 0;
}
}
@ -100,7 +141,7 @@ void Frame::Reset(int startIdx)
void Frame::Release()
{
for ( int i = 0; i < size; ++i )
Unref(frame[i]);
UnrefElement(i);
delete [] frame;
}
@ -145,7 +186,34 @@ Frame* Frame::Clone() const
return other;
}
Frame* Frame::SelectiveClone(const id_list& selection) const
static bool val_is_func(Val* v, BroFunc* func)
{
if ( v->Type()->Tag() != TYPE_FUNC )
return false;
return v->AsFunc() == func;
}
static Val* clone_if_not_func(Val** frame, int offset, BroFunc* func,
Frame* other)
{
auto v = frame[offset];
if ( ! v )
return nullptr;
if ( val_is_func(v, func) )
{
other->SetElement(offset, v, true);
return v;
}
auto rval = v->Clone();
other->SetElement(offset, rval);
return rval;
}
Frame* Frame::SelectiveClone(const id_list& selection, BroFunc* func) const
{
if ( selection.length() == 0 )
return nullptr;
@ -171,7 +239,7 @@ Frame* Frame::SelectiveClone(const id_list& selection) const
auto where = offset_map.find(std::string(id->Name()));
if ( where != offset_map.end() )
{
other->frame[where->second] = frame[where->second]->Clone();
clone_if_not_func(frame, where->second, func, other);
continue;
}
}
@ -179,7 +247,7 @@ Frame* Frame::SelectiveClone(const id_list& selection) const
if ( ! frame[id->Offset()] )
reporter->InternalError("Attempted to clone an id ('%s') with no associated value.", id->Name());
other->frame[id->Offset()] = frame[id->Offset()]->Clone();
clone_if_not_func(frame, id->Offset(), func, other);
}
/**
@ -379,6 +447,7 @@ std::pair<bool, Frame*> Frame::Unserialize(const broker::vector& data)
// Frame takes ownership of unref'ing elements in outer_ids
rf->outer_ids = std::move(outer_ids);
rf->closure = closure;
rf->weak_closure_ref = false;
for ( int i = 0; i < frame_size; ++i )
{
@ -437,7 +506,7 @@ void Frame::CaptureClosure(Frame* c, id_list arg_outer_ids)
closure = c;
if ( closure )
Ref(closure);
weak_closure_ref = true;
/**
* Want to capture closures by copy?