Is this well defined behavior?
#include <functional>
void foo() {
auto f = new std::function<void()>;
*f = [f]() { delete f; };
(*f)();
f = nullptr;
}
int main() {
foo();
}
Using the most recent g++, if I do this within a template it causes invalid reads while running under valgrind, otherwise it works fine. Why? Is this a bug in g++?
#include <functional>
template<std::size_t>
void foo() {
auto f = new std::function<void()>;
*f = [f]() { delete f; };
(*f)();
f = nullptr;
}
int main() {
foo<0>();
}
It will probably not crash in the a general case, but WHY on earth would you ever want to do something like that in the first place.
But here's my analysis:
valgrind produces:
This points at the code here (which is indeed the lambda function in your original code):
Interestingly, it "works" using clang++ (version 3.5, built from git sha1 d73449481daee33615d907608a3a08548ce2ba65, from March 31st):
Edit: It doesn't really make any sense - I don't see why there is a memory access to the first element inside the function class in gcc's code and not in clang's - they are supposed to do the same thing...
It is certainly not well-defined behaviour in general.
Between the end of execution of function object, and the end of the call to
operator()
, the memberoperator()
is executing on a deleted object. If the implementation reads or writes throughthis
, which it is perfectly allowed to do, then you'll get read or write of a deleted object.More specifically, the object was only just deleted by this very thread, so it's highly unlikely that any thread actually got around to using it between deletion and the read/write or it was unmapped, so it's quite unlikely to actually cause problems in a simple program. In addition, there's little apparent reason for the implementation to read or write to
this
after it returns.However, Valgrind is quite correct that any such read or write would be very invalid and in some circumstances, could lead to random crashes or memory corruption. It's easy to suggest that, between deleting
this
and a hypothetical read/write, this thread was pre-empted and another thread allocated and used that memory. Alternatively, the memory allocator decided it had enough cached memory of this size and returned this segment to the OS immediately upon freeing it. This is an excellent candidate for a Heisenbug since the conditions to cause it would be relatively rare and only apparent in real complex executing systems rather than trivial test programs.You could get away with it if you can prove that there are no reads or writes after the function object finishes returning. This basically means guaranteeing the implementation of
std::function<Sig>::operator()
.Edit:
Mats Peterson's answer raises an interesting question. GCC appears to have implemented the lambda by doing something like this:
As you can see, the call to
operator delete
makes a load froml
after it's just been deleted, which is exactly the scenario I described above. I'm not actually sure what C++11's memory model rules say about this, I would have thought it was illegal but not necessarily. It might not be defined either way. If it is not illegal, you are definitely screwed.Clang, however, seems to generate this operator:
Here when
l
is deleted it doesn't matter becausef
was copied into local storage.To a certain extent, this definitively answers your question- GCC absolutely does load from the memory of the lambda after it's been deleted. Whether or not that's Standard-legal or not, I'm not sure. You could definitely work around this by employing a user-defined function. You'd still have the problem of the std::function implementation issuing loads or stores to
this
, though.See http://cplusplus.github.io/LWG/lwg-active.html#2224.
Accessing a library type after its destructor has started is undefined behavior. Lambdas are not library types, so they don't have such a limitation. Once a destructor of a library type has been entered, the invariants of that library type no longer hold. The language doesn't enforce such a limitation because invariants are by and large a library concept, not a language concept.
This program has well-defined behavior and demonstrates a g++ bug.
The only questionable part of runtime is during the statement
(*f)();
. The behavior of that line can be picked apart piece by piece. The Standard section numbers below are from N3485; apologies if some don't match C++11.*f
is just the built-in unary operator on a raw pointer to class type. No problem here. The only other evaluation is the function-call expression(*f)()
, which invokesvoid std::function<void()>::operator() const
. Then that full-expression is a discarded value.20.8.11.2.4:
(I've replaced "
f
" in the Standard with "obj
" to reduce confusion withmain
'sf
.)Here
obj
is a copy of the lambda object,ArgTypes
is the empty parameter pack from the specializationstd::function<void()>
, andR
isvoid
.The
INVOKE
pseudo-macro is defined in 20.8.2. Since the type ofobj
is not a pointer-to-member,INVOKE
(obj, void)
is defined to beobj()
implicitly converted tovoid
.5.1.2p5:
... with exactly described declaration. In this case it turns out to be
void operator() const
. And its definition is exactly described too:5.1.2p7:
5.1.2p14:
5.1.2p17:
So the lambda function call operator must be equivalent to:
(where I've invented some names for the unnamed lambda type and unnamed data member.)
The single statement of that call operator is of course equivalent to
delete (*this).__unnamed_member_f;
So we have:operator*
dereference (on the prvaluethis
)delete
expressionstd::function<void()>::~function()
void operator delete(void*)
And finally, in 5.3.5p4:
(Here is where g++ is wrong, doing a second value computation on the member subobject between the destructor call and the deallocation function.)
This code cannot cause any other value computations or side effects after the
delete
expression.There are some allowances for implementation-defined behavior in lambda types and lambda objects, but none that affect anything above:
5.1.2p3:
The problem is not related to lambdas or std::function, but rather the semantics of delete. This code exhibits the same problem:
The issue is the semantics of delete. Is it allowed to load the operand again from memory after calling its destructor, in order to free its memory?
The allocation
auto f = new std::function<void()>;
of course is alright. The definition of the lambda*f = [f]() { delete f; };
works as well, as it is not executed yet.Now the interesting thing is
(*f)();
. First it dereferencesf
, then callsoperator()
and finally executesdelete f
. Callingdelete f
in the class member functionfunction<>::operator()
is the same as callingdelete this
. Under certain cirqumstances this is legal.So it depends on how
operator()
forstd::function
and lamdabs is implemented. Your code would be valid if it is guaranteed that no member function, member variable or the this pointer itself are used or even touched byoperator()
after executing your encapsulated lambda.I would say there is no need for
std::function
to call other member functions or use member variables inoperator()
after executing your lambda. So you will probably find implementations for which your code is legal, but in general it is probably not safe to assume so.