13

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>();
}
  • No, it isn't "well defined". By the way, what do you use it for? – alphashooter Apr 10 '14 at 20:51
  • My implementation of a templated shared pointer. I use the lambda to capture the type for deletion. The deleter also deletes itself after being invoked, since it's no longer needed. –  Apr 10 '14 at 20:59
  • Do you know which section of the standard mentions that this is undefined behavior? Because it seems to work with clang++ everywhere, and g++ outside of templates. –  Apr 10 '14 at 21:00
  • in addition, if g++ doesn't compile it, it's much better if you provide your code. – alphashooter Apr 10 '14 at 21:00
  • @clin18 I think that can be answered in the same way you're able to prove that the Tooth Fairy does not exist :) – San Jacinto Apr 10 '14 at 21:01
  • @alphashooter You need to use -std=c++11. –  Apr 10 '14 at 21:01
  • @SanJacinto If there were an ISO standard documenting the existence of mythical creatures, sure. –  Apr 10 '14 at 21:03
  • @clin18 because you try to delete function wrapper, and is it defined or undefined depends on implementation of std::function – alphashooter Apr 10 '14 at 21:04
  • @clin18 I was joking, but if the standard doesn't mention it, then it's by definition undefined. You're lucky if your undefined behavior is called out. – San Jacinto Apr 10 '14 at 21:04
  • @SanJacinto Well the standard gives a definition for lambdas, and std::function, I could simply apply those definitions. I think undefined behaviors are the exception, not the norm, when it comes to language specifications. –  Apr 10 '14 at 21:05
  • @clin18 `-std=c++11` is it really the answer on my question? or does it determine why the compilation breaks? :) – alphashooter Apr 10 '14 at 21:12
  • @alphashooter I must have misunderstood your question then. It should compile fine. The problem occurs at run time if the code appears in a template. –  Apr 10 '14 at 21:13
  • 2
    @alphashooter can you prove it? Where does the spec say that this has undefined behavior? – Johannes Schaub - litb Apr 10 '14 at 21:20
  • @alphashooter Actually, you'll need valgrind to even see the problem. There are invalid reads all over the place. –  Apr 10 '14 at 21:21
  • 3
    @SanJacinto the standard does not need to say that deleting a `std::function` is defined behavior. It is by default (as deleting a `std::string` is aswell). If this is undefined behavior, IMO it should need an explicit statement that deleting the object during invocation of the bound function object is undefined. – Johannes Schaub - litb Apr 10 '14 at 21:21
  • 1
    @JohannesSchaub-litb Deleting a `std::function` isn't, but how about doing it within a lambda owned by the `std::function`? –  Apr 10 '14 at 21:23
  • @clin18 Even now, it isn't clear for me, what is the reason you need to delete the object in this way. There is a vary of methods how you can avoid this, and even if this method works fine in some cases, it may not work in others. – alphashooter Apr 10 '14 at 21:24
  • 1
    @alphashooter At this point, the usefulness of this practice is meaningless to me. This question is more driven by curiosity than by practicality. –  Apr 10 '14 at 21:25
  • 2
    @clin18 Well, in this case I can say the following. In your example, deletion of `std::function` during its invokation isn't a good way. The only one situation when you can delete object by itself, directly or not, is when you are sure this object will NEVER be used. No non-static member functions calls, no access to its fields, NOTHING. If you are sure that all this conditions are true, you can use it, in any other case - you should not. – alphashooter Apr 10 '14 at 21:36
  • @alphashooter Sure, I'll null out the `f` right after. This isn't really a "good practice" problem, but a language specification problem. –  Apr 10 '14 at 21:38
  • Why would this point to a bug in g++? The templated version compiles/runs fine here (I tested it with g++ 4.6). That would be a problem with valgrind then. – Jonathan H Apr 10 '14 at 21:45
  • @clin18 But what does `f` stop to do it by itself? :) To the `f`, if it is deleted or not, it doesn't matter. I'm sure, in 99,9% attempts it would work. Maybe, even in 100%. But such things may cause problems. – alphashooter Apr 10 '14 at 21:47
  • Regarding the validity, I would say that there is no way to garantee that the delete statement would be the last statement in the body of the lambda function, so this is probably undefined. – Jonathan H Apr 10 '14 at 21:48
  • Well... I tested on my system with `-std=c++11` and `-std=c++0x` and it works (that is, it doesn't crash as mentioned in your "question".) I'm on Unbuntu 13.10 with the currently available version of g++ which is 4.8.1. What's yours btw? – Alexis Wilke Apr 10 '14 at 21:49
  • @alphashooter I'm having trouble understanding what you're saying. Check out the code again. The deletion occurs within a lambda. Which is managed by `f` but it is not `f`. –  Apr 10 '14 at 21:49
  • @Sh3ljohn I'm using 4.9. The problem is that it generates vastly different code apparently. It runs fine with g++ without templates, clang++ with or without templates, but not g++ with templates. –  Apr 10 '14 at 21:51
  • @Sh3ljohn I have explicitly made the `delete` the only statement. –  Apr 10 '14 at 21:52
  • @AlexisWilke It doesn't crash. Try running the template version under valgrind, you'll see invalid reads. –  Apr 10 '14 at 21:52
  • I know you have, made it the only statement, I was answering "is this well defined?". I don't think it is, because there is no way to ensure it would be the last statement. Also, 4.9 is not stable yet, so any "bug" you think you found might be fixed on the official release. – Jonathan H Apr 10 '14 at 21:55
  • @clin18 I said, if object is deleted directly or not, it has no sense. If you call the lambda directly, it's ok. But you call the lambda through `f`. – alphashooter Apr 10 '14 at 21:55
  • 1
    Any time you end an object's lifetime (at least of a type whose implementation you do not control) before the end of a member function call on that object, you run the risk that the member function will try to indirect through `this` between the destruction and the end of the call. The standard doesn't guarantee that `std::function` doesn't indirect through `this` between the invocation of the stored function object and the end of the call to `operator()`, so while it might work on one implementation, it certainly isn't portable. – Stuart Olsen Apr 10 '14 at 22:19
  • Looking at the code (see my answer), it would appear that the code is accessign something inside the function object after the destructor - which is bad. I'm using gcc 4.8.2 - the older 4.6.3 that I have from the installation of my Linux doesn't compile the code at all. – Mats Petersson Apr 10 '14 at 22:22
  • It's perfectly legal for the implementation to do that. Stuart Olsen has it right. – Puppy Apr 10 '14 at 22:32
  • 1
    http://stackoverflow.com/a/3150965/541686 – user541686 Apr 10 '14 at 22:45
  • 1
    @JohannesSchaub-litb Quote: `Undefined behavior may be expected when this International Standard omits any explicit definition of behavior or when a program uses an erroneous construct or erroneous data.` [link](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf). Is that behavior still defined? – alphashooter Apr 10 '14 at 22:47
  • 1
    I didn't have time to edit my last comment, but I wanted to say in addition, the Standard doesn't says if there's any guarantee that `std::function` would try to access `this` after invokation, the same is for lambda destructor. So it seems to be exactly undefined behavior. – alphashooter Apr 10 '14 at 22:57
  • @alphashooter are you saying that this is undefined behaior: `int foo = 0;` ? The Standard does not explicitly define behavior for a variable definition of name "foo". – Johannes Schaub - litb Apr 10 '14 at 23:40
  • @alphashooter OK the latter comment makes more sense to me.. but does it allow touching `this` after invocation? I mean, if it does touch `this`, then the program behavior becomes undefined if it deletes `this`. If it is a program that uses the component according to the Standard (and the Standard makes no explicit mention about not deleting `this` of the object, so I don't understand how to derive what is allowed and what not here), then a library implementation cannot simply render the program's behavior undefined. So my question is: what is the order of guarantees? – Johannes Schaub - litb Apr 10 '14 at 23:42
  • 1
    @JohannesSchaub-litb I will give an example in the context of this question. For `std::function`, the Standard says that this is a generalization of function objects and defines that the invokation must return the result which is the same as the passed functional object would return. Nothing more. You should understand, all this rules work until the cirqumstance is... «ideal». Calling `delete` during the invokation, you meddle in the object work and corrupt it. So you leave yourself a chance to get access violation. – alphashooter Apr 10 '14 at 23:58

6 Answers6

9

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 invokes void std::function<void()>::operator() const. Then that full-expression is a discarded value.

20.8.11.2.4:

R operator()(ArgTypes... args) const

Effects: INVOKE(obj, std::forward<ArgTypes>(args)..., R) where obj is the target object of *this.

(I've replaced "f" in the Standard with "obj" to reduce confusion with main's f.)

Here obj is a copy of the lambda object, ArgTypes is the empty parameter pack from the specialization std::function<void()>, and R is void.

The INVOKE pseudo-macro is defined in 20.8.2. Since the type of obj is not a pointer-to-member, INVOKE(obj, void) is defined to be obj() implicitly converted to void.

5.1.2p5:

The closure type for a lambda-expression has a public inline function call operator ...

... 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:

The lambda-expression's compound-statement yields the function-body of the function call operator, but for purposes of name lookup, determining the type and value of this and transforming id-expressions referring to non-static class members into class member access expressions using (*this), the compound-statement is considered in the context of the lambda-expression.

5.1.2p14:

For each entity captured by copy, an unnamed non-static data member is declared in the closure type.

5.1.2p17:

Every id-expression that is an odr-use of an entity captured by copy is transformed into an access to the corresponding unnamed data member of the closure type.

So the lambda function call operator must be equivalent to:

void __lambda_type::operator() const {
    delete __unnamed_member_f;
}

(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:

  • The built-in unary operator* dereference (on the prvalue this)
  • A member access expression
  • A value computation (aka lvalue-to-rvalue conversion) for the member subobject
  • A scalar delete expression
    • Invokes std::function<void()>::~function()
    • Invokes void operator delete(void*)

And finally, in 5.3.5p4:

The cast-expression in a delete-expression shall be evaluated exactly once.

(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:

An implementation may define the closure type differently from what is described below provided this does not alter the observable behavior of the program other than by changing:

  • the size and/or alignment of the closure type,

  • whether the closure type is trivially copyable,

  • whether the closure type is a standard-layout class, or

  • whether the closure type is a POD class.

Community
  • 1
  • 1
aschepler
  • 70,891
  • 9
  • 107
  • 161
  • Makes sense, why the difference between template and no template though? –  Apr 11 '14 at 01:43
  • 1
    To answer that you would need to dig through the g++ code. – aschepler Apr 11 '14 at 01:45
  • `R std::function::operator()(Args&&...) const` what have I missed if deletion of `this` through a constant function, even indirectly, is called well-defined? – alphashooter Apr 11 '14 at 03:45
  • 1
    This isn't literally calling `delete this`. it's deleting a pointer which happens to be equal to `this`. This is not a guarantee covered by `const`. Similarly, you could have a pointer to an object and a pointer to the same object as const, and call delete on the normal pointer. –  Apr 11 '14 at 04:00
  • 1
    This does not define whether or not the `std::function::operator()` operator can read or write through `this` after the INVOKE expression has returned. – Puppy Apr 11 '14 at 11:35
  • Well, it's `operator() const` so we know it can't write through `this`. –  Apr 11 '14 at 12:32
  • 1
    @DeadMG I think the standard is explicitly stating here that the `INVOKE` expression must be the only effect of the `operator()` call. –  Apr 11 '14 at 14:35
  • 1
    @DeadMG It doesn't say whether or not `std::function::operator()` can launch the US nuclear arsenal at Lichtenstein after the INVOKE expression has returned, either. It doesn't need to do so, because descriptions of behavior in the standard are taken to be total. – Casey Apr 12 '14 at 01:24
3

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 member operator() is executing on a deleted object. If the implementation reads or writes through this, 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:

struct lambda { std::function<void()>* f; };
void lambda_operator(lambda* l) {
    l->f->~std::function<void()>();
    ::operator delete(l->f);
}

As you can see, the call to operator delete makes a load from l 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:

void lambda_operator(lambda* l) {
    auto f = l->f;
    f->~std::function<void()>();
    ::operator delete(f);
}

Here when l is deleted it doesn't matter because f 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.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • 2
    "If the implementation reads or writes through this, which it is perfectly allowed to do". Why is it allowed to do so? After all, if it does, then the program's behavior becomes undefined! – Johannes Schaub - litb Apr 10 '14 at 23:45
  • @JohannesSchaub-litb Because it is the implementation of the standard library. It is well-formed, so it supposed to have predictable behavior. And if _you use_ it, you _must_ provide its behavior will defined in your program. _You_ use it. If it is bad for you that object allowed to read/write through `this` during member function invokation, it's your problem, not of the standard library, such as any other if it's well-formed. – alphashooter Apr 11 '14 at 00:57
  • 1
    @Johannes: There is no wording in the Standard banning the implementation from issuing reads and writes through `this` after the function object's `operator()` has returned. And it is the implementation so they can implement `operator()` however they like. No function can be assumed to be legal to be operating on a deleted object unless explicitly stated otherwise. – Puppy Apr 11 '14 at 11:33
  • @DeadMG *"There is no wording in the Standard banning the implementation from issuing reads and writes through this after the function object's operator() has returned."* - yes because we have "If a program contains no violations of the rules in this International Standard, a conforming implementation shall, within its resource limits, accept and correctly execute that program." – Johannes Schaub - litb Apr 11 '14 at 17:11
  • However, on std-discussion, Ville pointed to http://cplusplus.github.io/LWG/lwg-active.html#2224, which points out that we have a non-normative rule that forbids programs to delete a standard library object during the invocation of its member functions and which is going to be "normativated". Your answer would be better if you considered that this is a Standards defect but that the intent is that deleting the object is forbidden. – Johannes Schaub - litb Apr 11 '14 at 17:14
  • 1
    @JohannesSchaub-litb I don't believe that the resolution of 2224 would apply to this case. The normative statement "If an object of a standard library type **is accessed** [emphasis mine], and the beginning of the object's lifetime (3.8 [basic.life]) does not happen before the access, or the access does not happen before the end of the object's lifetime, the behavior is undefined unless otherwise specified." since - as explained in aschepler's answer - there is no access to the `std::function` after the invocation occurs in `operator()`. The actual access is within the lifetime proper. – Casey Apr 12 '14 at 01:31
  • 1
    @Casey on the std-discussion list, me and others have pointed this out aswell (which roots on the sloppy use of "access"). This is still an open issue report, so the solution proposed there does not necessarily reflect the committee's opinion by high probability (as is true with issue resolutions that are put into the spec). It's different with notes that *are* in the spec, which *do* express the committee's intent with high probability, just not *normatively*. And in our case, the note in the spec renders the code in this question undefined. – Johannes Schaub - litb Apr 12 '14 at 14:42
2

The problem is not related to lambdas or std::function, but rather the semantics of delete. This code exhibits the same problem:

class A;

class B {
    public:
        B(A *a_) : a(a_) {}
        void foo();
    private:
        A *const a;
};

class A {
    public:
        A() : b(new B(this)) {}
        ~A() {
            delete b;
        }
        void foo() { b->foo(); }
    private:
        B *const b;
};

void B::foo() {
    delete a;
}

int main() {
    A *ap = new A;
    ap->foo();
}

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?

kec
  • 2,099
  • 11
  • 17
  • 2
    Good find. In this case, it all comes down to 5.3.5p4 "The _cast-expression_ in a _delete-expression_ shall be evaluated exactly once." I read "evaluated" as including all the sorts of actions 3.8 forbids on a pointer to/glvalue referencing a destroyed object. – aschepler Apr 11 '14 at 01:48
  • 1
    @aschepler: I think your reading is right. This was actually in some code that clin18 submitted for a class that I teach. We were trying to track down if it was his code that was not compliant or g++ that was not compliant. (We knew that clang++ was fine with it.) – kec Apr 11 '14 at 02:00
2

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.

1

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:

==7323==    at 0x4008B5: _ZZ3fooILm0EEvvENKUlvE_clEv (in /home/MatsP/src/junk/a.out)
==7323==    by 0x400B4A: _ZNSt17_Function_handlerIFvvEZ3fooILm0EEvvEUlvE_E9_M_invokeERKSt9_Any_data (in /home/MatsP/src/junk/a.out)
==7323==    by 0x4009DB: std::function<void ()()>::operator()() const (in /home/MatsP/src/junk/a.out)
==7323==    by 0x40090A: void foo<0ul>() (in /home/MatsP/src/junk/a.out)
==7323==    by 0x4007E8: main (in /home/MatsP/src/junk/a.out)

This points at the code here (which is indeed the lambda function in your original code):

000000000040088a <_ZZ3fooILm0EEvvENKUlvE_clEv>:
  40088a:   55                      push   %rbp
  40088b:   48 89 e5                mov    %rsp,%rbp
  40088e:   48 83 ec 10             sub    $0x10,%rsp
  400892:   48 89 7d f8             mov    %rdi,-0x8(%rbp)
  400896:   48 8b 45 f8             mov    -0x8(%rbp),%rax
  40089a:   48 8b 00                mov    (%rax),%rax
  40089d:   48 85 c0                test   %rax,%rax   ;; Null check - don't delete if null. 
  4008a0:   74 1e                   je     4008c0 <_ZZ3fooILm0EEvvENKUlvE_clEv+0x36>
  4008a2:   48 8b 45 f8             mov    -0x8(%rbp),%rax
  4008a6:   48 8b 00                mov    (%rax),%rax
  4008a9:   48 89 c7                mov    %rax,%rdi
;; Call function destructor
  4008ac:   e8 bf ff ff ff          callq  400870 <_ZNSt8functionIFvvEED1Ev>  
  4008b1:   48 8b 45 f8             mov    -0x8(%rbp),%rax
  4008b5:   48 8b 00                mov    (%rax),%rax           ;; invalid access
  4008b8:   48 89 c7                mov    %rax,%rdi
;; Call delete. 
  4008bb:   e8 b0 fd ff ff          callq  400670 <_ZdlPv@plt>   ;; delete
  4008c0:   c9                      leaveq 
  4008c1:   c3                      retq   

Interestingly, it "works" using clang++ (version 3.5, built from git sha1 d73449481daee33615d907608a3a08548ce2ba65, from March 31st):

0000000000401050 <_ZZ3fooILm0EEvvENKUlvE_clEv>:
  401050:   55                      push   %rbp
  401051:   48 89 e5                mov    %rsp,%rbp
  401054:   48 83 ec 10             sub    $0x10,%rsp
  401058:   48 89 7d f8             mov    %rdi,-0x8(%rbp)
  40105c:   48 8b 7d f8             mov    -0x8(%rbp),%rdi
  401060:   48 8b 3f                mov    (%rdi),%rdi
  401063:   48 81 ff 00 00 00 00    cmp    $0x0,%rdi   ;; Null check. 
  40106a:   48 89 7d f0             mov    %rdi,-0x10(%rbp)
  40106e:   0f 84 12 00 00 00       je     401086 <_ZZ3fooILm0EEvvENKUlvE_clEv+0x36> 
  401074:   48 8b 7d f0             mov    -0x10(%rbp),%rdi
  401078:   e8 d3 fa ff ff          callq  400b50 <_ZNSt8functionIFvvEED2Ev>   
;; Function destructor 
  40107d:   48 8b 7d f0             mov    -0x10(%rbp),%rdi
  401081:   e8 7a f6 ff ff          callq  400700 <_ZdlPv@plt>    ;; delete. 
  401086:   48 83 c4 10             add    $0x10,%rsp
  40108a:   5d                      pop    %rbp
  40108b:   c3                      retq   

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...

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • 1
    Interesting- the compiler loaded `f` twice. I'm not sure that is legal under the C++11 memory model. – Puppy Apr 10 '14 at 22:44
  • Looking at the (rather messy - I'm sure for good reason) functional header file, there seem to be some _M_access involved (but maybe only if the function has actual arguments?). – Mats Petersson Apr 10 '14 at 23:19
0

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 dereferences f, then calls operator() and finally executes delete f. Calling delete f in the class member function function<>::operator() is the same as calling delete this. Under certain cirqumstances this is legal.

So it depends on how operator() for std::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 by operator() after executing your encapsulated lambda.

I would say there is no need for std::function to call other member functions or use member variables in operator() 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.

Danvil
  • 22,240
  • 19
  • 65
  • 88