gperftools: dynamically loading libtcmalloc is unsafe (was: Dynamic linking with tcmalloc is prone to crash)

libtcmalloc, designed to override weak symbols (malloc/free), can crash when late-loaded dynamically (with dl_open).

Consider the code in MallocExtension::Initialize:

  string dummy("I need to be allocated");
  dummy += "!";         // so the definition of dummy isn't optimized out

It calls into libstdc++'s basic_string allocation methods, which may have already been resolved to use libc’s malloc (if C++ strings were in use prior to the dl_open). Then the dtor/free calls the delete[] symbol directly and not via basic_string (probably due to inlining), which is newly loaded code, so is resolved to use tc_free.

This crashes with:

src/tcmalloc.cc:283] Attempt to free invalid pointer 0x55ef8c535fb0 

Using dl_open on ANY shared object which indirectly loads libtcmalloc - after instantiating std::string - will cause this crash.

In other words, the only safe use of libtcmalloc is via -l tcmalloc in the MAIN application, or via LD_PRELOAD. It is not safe to use libtcmalloc via -l tcmalloc in library code. That makes the library code dangerous to late-load.

Since late loading is a crucial technique for various programming languages (e.g: Python, Julia, Haskell in interpreted mode, …), crashing upon late load of tcmalloc is a problem.

I don’t know if there’s a good fix for this, except emphasizing in the documentation that -l tcmalloc is only supported in the main, and will cause this terrible bug otherwise.

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Reactions: 8
  • Comments: 21 (12 by maintainers)

Commits related to this issue

Most upvoted comments

I have a very similar crash when trying to run a golang application that is linked to a dynamic c++ library, itself linked with -ltcmalloc:

src/tcmalloc.cc:283] Attempt to free invalid pointer 0x10a8190

Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
51      ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) where 
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1  0x00007ffff7031801 in __GI_abort () at abort.c:79
#2  0x00007ffff5527fb9 in tcmalloc::Log(tcmalloc::LogMode, char const*, int, tcmalloc::LogItem, tcmalloc::LogItem, tcmalloc::LogItem, tcmalloc::LogItem) () from /usr/lib/x86_64-linux-gnu/libtcmalloc.so.4
#3  0x00007ffff551bd49 in ?? () from /usr/lib/x86_64-linux-gnu/libtcmalloc.so.4
#4  0x00007ffff553b6b9 in tc_free () from /usr/lib/x86_64-linux-gnu/libtcmalloc.so.4
#5  0x00007ffff5530720 in MallocExtension::Initialize() () from /usr/lib/x86_64-linux-gnu/libtcmalloc.so.4
#6  0x00007ffff551ae0a in ?? () from /usr/lib/x86_64-linux-gnu/libtcmalloc.so.4
#7  0x00007ffff7de5733 in call_init (env=0x7fffffffdc18, argv=0x7fffffffdc08, argc=1, l=<optimized out>) at dl-init.c:72
#8  _dl_init (main_map=0x7ffff7ffe170, argc=1, argv=0x7fffffffdc08, env=0x7fffffffdc18) at dl-init.c:119
#9  0x00007ffff7dd60ca in _dl_start_user () from /lib64/ld-linux-x86-64.so.2
#10 0x0000000000000001 in ?? ()
#11 0x00007fffffffdfe0 in ?? ()
#12 0x0000000000000000 in ?? ()

This is on Ubuntu 18. golang 1.11.2

I can make the crash go away if I explicitly link the go application against tcmalloc (add a -ltcmalloc somewhere in the cgo build process).