ASAN detects ODR violation of vtable of class which is shared with dynamically loaded library
Asked Answered
J

1

6

I'm working on a project which has a "util" library containing stuff like logging, assertion handling etc. This is compiled into a static library with -fPIC added. I also have a plugin system, where the plugins are shared libraries loaded at runtime via dlopen. Those plugins and the main executable both use the static util library.

The problem: Now I'm getting AddressSanitizer: odr-violation errors when using ASAN. The issue is size=40 'vtable for StdStreamWriter' reported twice where StdStreamWriter is an implementation of an interface used internally in the static library.

I tried really hard to reproduce this in a MWE:

  • Create a static library exposing some function
  • Inside that use an interface and implementation in a std::shared_ptr
  • create a shared library linking against that
  • create an executable linking against the static lib and dlopen the shared library

CMakeLists.txt

cmake_minimum_required(VERSION 3.8)
project(proj)

set(sanitizer_flags "-fsanitize=address,undefined -fno-omit-frame-pointer")
string(APPEND CMAKE_CXX_FLAGS " ${sanitizer_flags}")
string(APPEND CMAKE_EXE_LINKER_FLAGS " ${sanitizer_flags}")
string(APPEND CMAKE_MODULE_LINKER_FLAGS " ${sanitizer_flags}")

add_library(foo STATIC foo.cpp)
target_compile_features(foo PUBLIC cxx_std_14)
set_target_properties(foo PROPERTIES CXX_EXTENSIONS OFF POSITION_INDEPENDENT_CODE ON)
add_library(lib SHARED lib.cpp)
target_link_libraries(lib foo)
add_executable(main main.cpp)
target_link_libraries(main foo dl)

However the issue does not occur in the MWE no matter how hard I try.

I traced the difference down to differing results in nm -C liblib.so | grep vtable:

  • The MWE (no ODR error) shows V vtable for Impl
  • The acutal program shows D vtable for StdStreamWriter

I guess the error stems from the difference D vs V which results in the vtables not being merged.

Where does this difference come from? When is this decided? I stripped down the link command for the shared library to the bare essentials (clang++-8 -shared -fsanitize=address,undefined -o <..> <all *.o and *.so>) but still get the D vtable instead of V vtable.

What else can I try to fix this?

Joint answered 7/8, 2019 at 8:58 Comment(7)
I suggest to file a bug in Asan tracker (they'll ask you to repro w/ latest Clang).Hypoxia
I wish I could create a repro that would help me investigate and probably solve the issue. But any MWE I tried doesn't run into that D vtable thingJoint
I know it's been a year but was this Clang or GCC?Hypoxia
It is Clang, see the second last paragraphJoint
Could you try compiling with -mllvm -asan-use-private-alias=1 then? I'll prepare full answer if it works.Hypoxia
It seems to work in our real codebaseJoint
Could you +1 the answer then? BTW I've deleted my answer in another question and voted to close as dup.Hypoxia
H
5

This is most likely caused by a known issue in Clang's implementation of Asan which causes it to detect false ODR violations for static data with vague linkage (normally class vtables or typeinfos).

As a workaround, try compiling with -mllvm -asan-use-private-alias=1 and maybe doing export ASAN_OPTIONS=use_odr_indicator=1 before running your code.

If this fixes your problem, please post a comment in the aforementioned issue to increase the chance that it's fixed once and for all in upstream.

Hypoxia answered 25/5, 2020 at 16:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.