Here is that I think happens. Your allocator redefines a set of
allocation/free function, but tsan redefines more of them. As the
result, an allocation request goes to your allocator, while free
request goes to tsan allocator. Without tsan it works because the free
function forwards to one of the free functions that you intercept;
e.g. you intercept free() but not operator delete[], standard
implementation of operator delete[] simply calls free(), so the
execution goes to your allocator anyway. Tsan intercepts operator
delete[], and so execution do not go into your allocator.
As a side note, the crash can mean you have alloc/dealoc mismatch.
E.g. you allocate memory with operator new[] and then free it with
operator delete. This is a bug. It should be detectable by asan.
If possible, disable your custom allocator under tsan. We do this both
in Chromium and our internal codebase. By default they use tcmalloc,
but under sanitizers we turn off tcmalloc. This will eliminate the
crash and also give better race detection and reports.