Valgrind errors with boost::thread_specific_ptr on GCC 8.3 + Linux

Rob*_*ser 10 c++ boost pthreads boost-thread thread-local

  • Ubuntu 19 running inside Docker
  • GCC 8.3
  • Boost 1.69
  • Valgrind 3.14.0

When the application is shutting down Valgrind reports these 3 issues:

==70== Mismatched free() / delete / delete []
==70==    at 0x483997B: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==70==    by 0x4870C89: check_free (dlerror.c:202)
==70==    by 0x4870C89: check_free (dlerror.c:186)
==70==    by 0x4870C89: free_key_mem (dlerror.c:221)
==70==    by 0x4870C89: __dlerror_main_freeres (dlerror.c:239)
==70==    by 0x4B59711: __libc_freeres (in /usr/lib/x86_64-linux-gnu/libc-2.29.so)
==70==    by 0x482E19E: _vgnU_freeres (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_core-amd64-linux.so)
==70==    by 0x4A0A3A9: __run_exit_handlers (exit.c:132)
==70==    by 0x4A0A3D9: exit (exit.c:139)
==70==    by 0x49E9B71: (below main) (libc-start.c:342)
==70==  Address 0x4f6a570 is 0 bytes inside a block of size 312 alloc'd
==70==    at 0x4838DBF: operator new(unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==70==    by 0x303D6D: boost::detail::make_external_thread_data() (in /build-context/bin/debug/setmatch-tests)
==70==    by 0x305424: boost::detail::add_new_tss_node(void const*, boost::shared_ptr<boost::detail::tss_cleanup_function>, void*) (in /build-context/bin/debug/setmatch-tests)
==70==    by 0x3054ED: boost::detail::set_tss_data(void const*, 

[...]

==70== Invalid free() / delete / delete[] / realloc()
==70==    at 0x483997B: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==70==    by 0x4870BB4: free_key_mem (dlerror.c:223)
==70==    by 0x4870BB4: __dlerror_main_freeres (dlerror.c:239)
==70==    by 0x4B59711: __libc_freeres (in /usr/lib/x86_64-linux-gnu/libc-2.29.so)
==70==    by 0x482E19E: _vgnU_freeres (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_core-amd64-linux.so)
==70==    by 0x4A0A3A9: __run_exit_handlers (exit.c:132)
==70==    by 0x4A0A3D9: exit (exit.c:139)
==70==    by 0x49E9B71: (below main) (libc-start.c:342)
==70==  Address 0x4f6a570 is 0 bytes inside a block of size 312 free'd
==70==    at 0x483997B: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==70==    by 0x4870C89: check_free (dlerror.c:202)
==70==    by 0x4870C89: check_free (dlerror.c:186)
==70==    by 0x4870C89: free_key_mem (dlerror.c:221)
==70==    by 0x4870C89: __dlerror_main_freeres (dlerror.c:239)
==70==    by 0x4B59711: __libc_freeres (in /usr/lib/x86_64-linux-gnu/libc-2.29.so)
==70==    by 0x482E19E: _vgnU_freeres (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_core-amd64-linux.so)
==70==    by 0x4A0A3A9: __run_exit_handlers (exit.c:132)
==70==    by 0x4A0A3D9: exit (exit.c:139)
==70==    by 0x49E9B71: (below main) (libc-start.c:342)
==70==  Block was alloc'd at
==70==    at 0x4838DBF: operator new(unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==70==    by 0x303D6D: boost::detail::make_external_thread_data() (in /build-context/bin/debug/setmatch-tests)
==70==    by 0x305424: boost::detail::add_new_tss_node(void const*, boost::shared_ptr<boost::detail::tss_cleanup_function>, void*) (in /build-context/bin/debug/setmatch-tests)
==70==    by 0x3054ED: boost::detail::set_tss_data(void const*, boost::shared_ptr<boost::detail::tss_cleanup_function>, void*, bool) (in /build-context/bin/debug/setmatch-tests)
==70==    by 0x188841: boost::thread_specific_ptr<burningmime::setmatch::MatchState>::reset(burningmime::setmatch::MatchState*) (tss.hpp:105)

[...]

==70== 24 bytes in 1 blocks are definitely lost in loss record 1 of 2
==70==    at 0x4838DBF: operator new(unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==70==    by 0x303F50: boost::detail::make_external_thread_data() (in /build-context/bin/debug/setmatch-tests)
==70==    by 0x305424: boost::detail::add_new_tss_node(void const*, boost::shared_ptr<boost::detail::tss_cleanup_function>, void*) (in /build-context/bin/debug/setmatch-tests)
==70==    by 0x3054ED: boost::detail::set_tss_data(void const*, boost::shared_ptr<boost::detail::tss_cleanup_function>, void*, bool) (in /build-context/bin/debug/setmatch-tests)

[...]
Run Code Online (Sandbox Code Playgroud)

It looks like boost is allocating its thread data in the same place that dlerror has allocated its own thread data. A quick search points to a (slightly different?) version of dlerror here

A quick glance over at bosot's code looks to me like it's just allocating the TSS block on the heap.

This was not a problem with GCC 7.3.0 + Ubuntu 18 (same Boost version)

Anyone have some insight here?

EDIT: Maybe it's the double-free that was fixed in this commit? Still I don't see why Boost would be using that at all.

Flo*_*mer 2

如果我像这样修改调用周围的glibc 上游测试用例pthread_setspecific(并使用 编译它g++):

    void *ptr = new char;
    printf("Setting thread local to ptr.\n");
    if (pthread_setspecific(key, ptr) != 0) {
      perror("pthread_setspecific");
      exit(1);
    }
    delete ptr;
Run Code Online (Sandbox Code Playgroud)

在修复之前(在提交 5b06f538c5aee0389ed034f60d90a8884d6d54de 处,使用./testrun.sh --tool=valgrind /path/to/testglibc 构建树)运行 glibc 时,我收到此错误:

    void *ptr = new char;
    printf("Setting thread local to ptr.\n");
    if (pthread_setspecific(key, ptr) != 0) {
      perror("pthread_setspecific");
      exit(1);
    }
    delete ptr;
Run Code Online (Sandbox Code Playgroud)

这与您遇到的错误几乎相同,减去operator newBoost 中分配的嵌套。所以看起来这两个错误确实是相同的。

这是有道理的:由于bug 24476libdl使用未初始化的pthread_key_t值(之前没有调用pthread_key_create它)。对于数据段(其中内部密钥libdl存储为0,当然,未初始化意味着零,并且正如您从测试中的诊断输出中看到的那样,测试分配的密钥(在您的情况下是Boost)实际上是密钥0 :

key = 0
Run Code Online (Sandbox Code Playgroud)

这段libdl代码相当复杂,我发布了一个补丁,该补丁dlerror移至 libc(来自 libdl),并且还完全避免使用 POSIX 线程线程本地存储。

总结一下:无论谁维护您使用的 glibc 版本,都需要将上游修复程序向后移植到其源代码树中并发布更新。 我们也必须这样做。从好的方面来说,这个错误仅当您在 valgrind 和类似工具下运行应用程序时才会发生,因为在常规进程关闭期间,__libc_freeres不会调用该错误:进程无论如何都会很快退出,并且内核会为我们清理所有资源。除非您在生产中使用 valgrind,否则这意味着您永远不会遇到此错误。当然,当你使用 valgrind 进行调试时,这仍然是一个恼人的问题。对于那个很抱歉。