Ошибки Valgrind с boost::thread_specific_ptr в GCC 8.3 + Linux

  • Ubuntu 19 работает внутри Docker
  • ССЗ 8.3
  • Повышение 1.69
  • Валгринд 3.14.0

Когда приложение закрывается, Valgrind сообщает об этих трех проблемах:

==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)

[...]

Похоже, что boost выделяет данные своего потока в том же месте, где dlerror выделяет свои собственные данные потока. Быстрый поиск указывает на (немного отличающуюся?) версию dlerror здесь

Беглый взгляд на код bosot показался мне просто выделение блока TSS в куче.

Это не было проблемой с GCC 7.3.0 + Ubuntu 18 (та же версия Boost)

У кого-нибудь есть понимание здесь?

EDIT: Возможно, было исправлено двойное освобождение в этом коммите? Тем не менее я не понимаю, зачем Boost вообще это использует.


person Robert Fraser    schedule 26.09.2019    source источник
comment
Есть ли причина, по которой вы не используете последнюю версию Valgrind 3.15.0?   -  person entpnerd    schedule 08.10.2019


Ответы (3)


Если я изменю исходный тестовый пример 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;

Я получаю эту ошибку при работе с glibc непосредственно перед исправлением (при фиксации 5b06f538c5aee0389ed034f60d90a8884d6d54de, используя ./testrun.sh --tool=valgrind /path/to/test из дерева сборки glibc):

==14143== Invalid read of size 8
==14143==    at 0x483B550: check_free (dlerror.c:188)
==14143==    by 0x483BA21: free_key_mem (dlerror.c:221)
==14143==    by 0x483BA21: __dlerror_main_freeres (dlerror.c:239)
==14143==    by 0x4D06AD1: __libc_freeres (in /home/fweimer/src/gnu/glibc/build/libc.so)
==14143==    by 0x48031DE: _vgnU_freeres (vg_preloaded.c:77)
==14143==    by 0x4BDD331: __run_exit_handlers (exit.c:132)
==14143==    by 0x4BDD3C9: exit (exit.c:139)
==14143==    by 0x4BC7E21: (below main) (libc-start.c:342)
==14143==  Address 0x4d750d8 is 23 bytes after a block of size 1 free'd
==14143==    at 0x480CEFC: operator delete(void*) (vg_replace_malloc.c:586)
==14143==    by 0x401344: main (t.c:93)
==14143==  Block was alloc'd at
==14143==    at 0x480BE86: operator new(unsigned long) (vg_replace_malloc.c:344)
==14143==    by 0x4012F4: main (t.c:87)
==14143== 
==14143== Invalid free() / delete / delete[] / realloc()
==14143==    at 0x480CA0C: free (vg_replace_malloc.c:540)
==14143==    by 0x483BA29: free_key_mem (dlerror.c:223)
==14143==    by 0x483BA29: __dlerror_main_freeres (dlerror.c:239)
==14143==    by 0x4D06AD1: __libc_freeres (in /home/fweimer/src/gnu/glibc/build/libc.so)
==14143==    by 0x48031DE: _vgnU_freeres (vg_preloaded.c:77)
==14143==    by 0x4BDD331: __run_exit_handlers (exit.c:132)
==14143==    by 0x4BDD3C9: exit (exit.c:139)
==14143==    by 0x4BC7E21: (below main) (libc-start.c:342)
==14143==  Address 0x4d750c0 is 0 bytes inside a block of size 1 free'd
==14143==    at 0x480CEFC: operator delete(void*) (vg_replace_malloc.c:586)
==14143==    by 0x401344: main (t.c:93)
==14143==  Block was alloc'd at
==14143==    at 0x480BE86: operator new(unsigned long) (vg_replace_malloc.c:344)
==14143==    by 0x4012F4: main (t.c:87)

Это почти та же ошибка, что и у вас, за исключением вложения распределения operator new в Boost. Так что похоже, что эти две ошибки действительно одинаковы.

Это имеет смысл: из-за ошибки 24476 libdl использует неинициализированное значение pthread_key_t ( без предварительного вызова pthread_key_create на нем). Для сегмента данных (где хранится внутренний ключ для libdl0, неинициализированный означает ноль, конечно, и как вы можете видеть из диагностического вывода в тесте, ключ, выделенный тестом (и Boost в вашем случае), на самом деле был ключом 0:

key = 0

Этот libdl код довольно запутан, и я опубликовал патч, который перемещает dlerror в libc (из libdl), а также полностью избегает использования локального хранилища потоков POSIX.

Подводя итог: тот, кто поддерживает используемую вами версию glibc, должен сделать резервную копию первоначальное исправление в свое исходное дерево и выпустить обновление. Мы также должны были сделать это. Положительным моментом является только эта ошибка. происходит, когда вы запускаете свое приложение под valgrind и подобными инструментами, потому что при обычном завершении процесса __libc_freeres не вызывается: процесс все равно скоро завершится, и ядро ​​​​очищает для нас все ресурсы. Если вы не используете valgrind в рабочей среде, это означает, что вы никогда не столкнетесь с этой ошибкой. Конечно, это все еще раздражает, когда вы используете valgrind для отладки. Извини за это.

person Florian Weimer    schedule 08.10.2019
comment
Спасибо!! Приятно слышать от кого-то, кто действительно работал над этим материалом. - person Robert Fraser; 09.10.2019
comment
Я так боюсь, что функции перейдут в glibc. clock_gettime() переход с librt на libc сильно нарушил обратную совместимость. - person Robert Fraser; 09.10.2019
comment
@RobertFraser Пожалуйста, сообщите об ошибке или отправьте сообщение в libc-help, если что-то не работает. - person Florian Weimer; 09.10.2019

Пожалуйста, проверьте версию всех инструментов, которые вы использовали. Похоже, в этом есть какая-то проблема совместимости версий. Попробуйте использовать valgrind версии 3.15.0.

Подробнее об использовании valgrind см. здесь.

person Mohinuddin Luhar    schedule 08.10.2019

Возможно, вам следует обновить версию valgrind до 3.15.0, это должно помочь.

Я думаю, что здесь должно вам помочь.

person trytocode    schedule 08.10.2019