Skip to content

Memory leak in thread_create11 #986

@ivanallen

Description

@ivanallen

You can reproduce it with following code:

TEST(thread11, test2)
{
    struct Foo {
        Foo() {
            std::cout << this << ":Foo ctor" << std::endl;
        }
        Foo(const Foo&) {
            std::cout << this << ":Foo copy ctor" << std::endl;
        }
        Foo(Foo&&) = delete;
        // Foo(Foo&&) {
        //     std::cout << this << ":Foo move ctor" << std::endl;
        // }
        ~Foo() {
            std::cout << this << ":Foo dtor" << std::endl;
        }
        int x = 100;
    };
    {
        Foo foo;
        std::cout << "thread_create11" << std::endl;
        auto th = thread_create11([foo]{
            std::cout << &foo << ":thread" << std::endl;
            std::cout << foo.x << std::endl;
        });
        auto jh = thread_enable_join(th);
        thread_join(jh);
    }
    std::cout << "end" << std::endl;
}

There are 4 ctor invoked, but only 3 dtor invoked.

output:

[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from thread11
[ RUN      ] thread11.test2
0x7fff42cd2460:Foo ctor
thread_create11
0x7fff42cd2470:Foo copy ctor
0x7fff42cd2390:Foo copy ctor
0x7f5573cfbbb8:Foo copy ctor
0x7fff42cd2390:Foo dtor
0x7fff42cd2470:Foo dtor
0x7f5573cfbbb8:thread
100
0x7fff42cd2460:Foo dtor
end
[       OK ] thread11.test2 (1 ms)
[----------] 1 test from thread11 (1 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (1 ms total)
[  PASSED  ] 1 test.
Tracer caught signal 11: addr=0x7f5573cff000 pc=0x516cf0 sp=0x7f55734f9c90
==3367528==LeakSanitizer has encountered a fatal error.
==3367528==HINT: For debugging, try setting environment variable LSAN_OPTIONS=verbosity=1:log_threads=1
==3367528==HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc)

There is a bug in the implementation of __thread_create11:

template<typename F, typename SavedArgs, typename...ARGUMENTS> inline
thread* __thread_create11(uint64_t stack_size, F&& f, ARGUMENTS&&...args) {
    using Pair = std::pair<F, SavedArgs>;
    static_assert(sizeof(Pair) < UINT16_MAX, "...");
    auto th = thread_create(&__stub11<Pair>, nullptr, stack_size, sizeof(Pair));
    auto p  = thread_reserved_space<Pair>(th);

    // Here, placement new is called only on the address of p, but no destructor is called anywhere.
    // A memory leak occurred if F or ARGUMENTS need to destruct some resource in its destructor method
    new (p) Pair{std::forward<F>(f), SavedArgs{std::forward<ARGUMENTS>(args)...}};
    return th;
}

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions