[BUG] False positive vptr errors with shared ubsan runtimes(which is the default)
Description
Env Info:
【Compile Sdk Version】33 【NDK Version】R21e 【JDK】Java 8
Error Info:
2024-08-29 09:55:10.324 18568-18568 app_process64 com.cwx.inanna_android I /Users/cwx/work/codes/gitCodes/inanna/inanna-framework/demos/android/inanna-android/app/src/main/cpp/native-lib.cpp:42:22: runtime error: member call on address 0x0041793d7bf0 which does not point to an object of type 'Base'
2024-08-29 09:55:10.324 18568-18568 app_process64 com.cwx.inanna_android I 0x0041793d7bf0: note: object is of type 'Derived'
2024-08-29 09:55:10.324 18568-18568 app_process64 com.cwx.inanna_android I 00 00 00 00 f0 95 db 13 71 00 00 00 00 00 00 00 be be be be 02 11 00 00 10 00 00 00 5b 27 00 00
2024-08-29 09:55:10.324 18568-18568 app_process64 com.cwx.inanna_android I ^~~~~~~~~~~~~~~~~~~~~~~
2024-08-29 09:55:10.324 18568-18568 app_process64 com.cwx.inanna_android I vptr for 'Derived'
C++ Code:
// Base.h
class Base {
public:
virtual ~Base();
virtual int foo();
int bar();
};
// Base.cpp
Base::~Base()
{
}
int Base::foo()
{
return 1;
}
int Base::bar() {
return 2;
}
// Derived.h
class Derived : public Base {
public:
int foo() override;
};
// Derived.cpp
int Derived::foo() {
return bar() + Base::foo();
}
int init() {
Base *pBase = new Derived();
int ret = pBase->foo();
delete pBase;
return ret;
}
### cmake options:
target_compile_options(inanna_android PUBLIC -fsanitize=address -fno-omit-frame-pointer)
target_link_options(inanna_android PUBLIC -fsanitize=address)
target_compile_options(inanna_android PUBLIC -fsanitize=undefined -fno-sanitize-recover=undefined)
target_link_options(inanna_android PUBLIC -fsanitize=undefined -fno-sanitize-recover=undefined)
described:
When I turned on one of the options separately, it worked well.Or if I turn off the vptr check, it can work well,but when i both open ASAN and UBASAN,the error coming...
Affected versions
Canary
Canary version
NDKr21e
Host OS
Mac
Host OS version
14.2.1
Affected ABIs
arm64-v8a
Build system
CMake
Other build system
No response
minSdkVersion
24
Device API level
No response
vptr check is part of ubsan, not sure why enabling ubsan doesn't trigger it. You might want to open a bug in llvm-project/issues for clarification
vptr check is part of ubsan, not sure why enabling ubsan doesn't trigger it. You might want to open a bug in llvm-project/issues for clarification
but asan*.so is in NDK
vptr check is part of ubsan, not sure why enabling ubsan doesn't trigger it. You might want to open a bug in llvm-project/issues for clarification
can i make vpt check no crash in android by use wrap.sh?
NDKr21e
That NDK is three and a half years old (and is based on an LLVM that's nearly five). There's a pretty good chance that the bug has been fixed since then. Have a look at https://github.com/android/ndk-samples/tree/main/sanitizers for the recommended way to use ubsan in apps. Note that ASan isn't supported any more, since HWASan is better in almost every way: http://go/android-dev/ndk/guides/memory-debug
If you update to NDK r27 and UBSan still isn't able to catch the bug without ASan, attach a complete test case and we can re-open. It may just be the case that UBSan isn't able to catch this bug in every case though. There are a number of UBSan checks that can't be caught 100% reliably, but idk if this is one of them.
恩德克尔21e
该 NDK 已有三年半历史(并且基于近五年的 LLVM)。从那时起,该错误很有可能已经得到修复。请查看https://github.com/android/ndk-samples/tree/main/sanitizers,了解在应用中使用 ubsan 的推荐方法。请注意,ASan 不再受支持,因为 HWASan 几乎在各个方面都更胜一筹:http://go/android-dev/ndk/guides/memory-debug
如果您更新到 NDK r27 并且 UBSan 仍然无法在没有 ASan 的情况下捕获错误,请附上完整的测试用例,我们可以重新打开。不过,UBSan 可能并不是在所有情况下都能捕获此错误。有许多 UBSan 检查无法 100% 可靠地捕获,但我不知道这是否是其中之一。
I tried to upgrade to NDK r27, but there was still a problem.😢
I'm sorry, there may be problems with my previous statement. This crash has nothing to do with whether to turn on ASan. The main problem is UBSan's vpr check.
If I turn on the compilation options below, a crash will occur:
-fsanitize=undefined -fno-sanitize-recover=undefined
But when I turned off the vpr check, everything was fine:
-fsanitize=undefined -fno-sanitize-recover=undefined -fno-sanitize=vptr
If I turn on the compilation options below, a crash will occur: -fsanitize=undefined -fno-sanitize-recover=undefined But when I turned off the vpr check, everything was fine: -fsanitize=undefined -fno-sanitize-recover=undefined -fno-sanitize=vptr
Isn't this is the intended behavior? this bug is specifically caught by vptr sanitizer.
Yeah, that's what -fno-sanitize-recover does: UBSan failures will crash the program.
If I turn on the compilation options below, a crash will occur: -fsanitize=undefined -fno-sanitize-recover=undefined But when I turned off the vpr check, everything was fine: -fsanitize=undefined -fno-sanitize-recover=undefined -fno-sanitize=vptr
Isn't this is the intended behavior? this bug is specifically caught by vptr sanitizer.
You can take a look at the C++ code I attached at the beginning. This is the most common use of C++ polymorphism and should not be detected as an error; and I work normally on other platforms
Yeah, that's what
-fno-sanitize-recoverdoes: UBSan failures will crash the program.
But my code should not be detected as errors
Are you using fvisibility flag somewhere? there is a similar bug reported in: https://bugs.llvm.org/show_bug.cgi?id=39191
Are you using fvisibility flag somewhere? there is a similar bug reported in: https://bugs.llvm.org/show_bug.cgi?id=39191
In fact, all my inheritances are in the same library, I didn't make calls across libraries, and I didn't proactively turn off visibility
Are you using fvisibility flag somewhere? there is a similar bug reported in: https://bugs.llvm.org/show_bug.cgi?id=39191
You can download the zip I sent earlier and check my compilation options
As the current behavior of vptr relates to open source implementation of this sanitizer, it is better to create an issue in the llvm-project/issues repo.
As the current behavior of vptr relates to open source implementation of this sanitizer, it is better to create an issue in the llvm-project/issues repo.
I have already asked a question on this page, but no one responded😢
Link?
Link to what?
Link to what?
The other bug you filed that you said has no response. We may be able to help with that.
Link to what?
The other bug you filed that you said has no response. We may be able to help with that.
here:https://github.com/llvm/llvm-project/issues/106933 plz.
Adding some folks from the sanitizer team: @eugenis @fmayer
I was able to reproduce this on Android with the following command (nb: also push libc++_shared.so.)
$ /usr/local/google/work/android-ndk-r27/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android35-clang++ san.cpp -fsanitize=undefined
Adding -static-libsan to use the static libubsan library seems fine. This did not reproduce on Linux with both shared and static ubsan runtimes.
Reduced test case:
struct Base {
virtual int foo();
};
int Base::foo() {
return 1;
}
int main() {
Base obj;
obj.foo();
return 0;
}
adb shell 'rm -fr /data/local/tmp/*' && /x/android-ndk-r27/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android35-clang++ -O2 san.cpp -fsanitize=undefined && adb push /x/android-ndk-r27/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/x86_64-linux-android/libc++_shared.so /data/local/tmp && adb push /x/android-ndk-r27/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/18/lib/linux/libclang_rt.ubsan_standalone-x86_64-android.so /data/local/tmp && adb push a.out /data/local/tmp && adb shell LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/a.out
/x/android-ndk-r27/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/x86_64-linux-android/libc++_shared.so: 1 file pushed, 0 skipped. 334.2 MB/s (1617608 bytes in 0.005s)
/x/android-ndk-r27/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/18/lib/linux/libclang_rt.ubsan_standalone-x86_64-android.so: 1 file pushed, 0 skipped. 273.2 MB/s (870032 bytes in 0.003s)
a.out: 1 file pushed, 0 skipped. 69.6 MB/s (6376 bytes in 0.000s)
san.cpp:11:9: runtime error: member call on address 0x7ffc8754a5b0 which does not point to an object of type 'Base'
0x7ffc8754a5b0: note: object is of type 'Base'
58 5b 00 00 a0 09 9e 37 58 5b 00 00 83 ec 0f de d7 7e 00 00 00 00 00 00 00 00 00 00 00 00 00 00
^~~~~~~~~~~~~~~~~~~~~~~
vptr for 'Base'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior san.cpp:11:9 in
Presumably the problem here is that the UBSan runtime is referencing libc++[abi] symbols and relying on those symbols having the same address across the relevant modules of code. For example:
$ nm a.out | grep class_type_info | c++filt
U vtable for __cxxabiv1::__class_type_info
$ nm /x/android-ndk-r27/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/18/lib/linux/libclang_rt.ubsan_standalone_cxx-x86_64-android.a | grep class_type_info | c++filt
0000000000000000 t findBaseAtOffset(__cxxabiv1::__class_type_info const*, long)
0000000000000000 t isDerivedFromAtOffset(__cxxabiv1::__class_type_info const*, __cxxabiv1::__class_type_info const*, long)
U typeinfo for __cxxabiv1::__class_type_info
U typeinfo for __cxxabiv1::__si_class_type_info
U typeinfo for __cxxabiv1::__vmi_class_type_info
The vtable for Base refers to __cxxabiv1::__class_type_info's vtable, and the UBSan runtime refers to the typeinfo for __cxxabiv1::__class_type_info, but the two __class_type_info classes are (probably) considered different classes because the copy in the shared ubsan runtime isn't exported.
I think the Android LLVM's glibc build turns off this part of UBSan (SANITIZER_ALLOW_CXXABI is OFF):
https://android.googlesource.com/toolchain/llvm_android/+/refs/heads/llvm-r536225-release/src/llvm_android/base_builders.py#888
The clang-19 on my machine (Rodete/gLinux/Debian), OTOH, has a UBSan shared library that imports the C++ ABI symbols from libstdc++.so.6:
$ readelf -d /usr/lib/llvm-19/lib/clang/19/lib/linux/libclang_rt.ubsan_standalone-x86_64.so
...
0x0000000000000001 (NEEDED) Shared library: [libstdc++.so.6]
...
$ nm -D /usr/lib/llvm-19/lib/clang/19/lib/linux/libclang_rt.ubsan_standalone-x86_64.so | c++filt | grep class_type_info
U typeinfo for __cxxabiv1::__class_type_info@CXXABI_1.3
U typeinfo for __cxxabiv1::__si_class_type_info@CXXABI_1.3
U typeinfo for __cxxabiv1::__vmi_class_type_info@CXXABI_1.3
This clang-19 defaults to a static UBSan runtime -- the shared runtime isn't in the default library search path, but I can switch to the shared runtime and it works:
$ clang++-19 san.cpp -O2 -fsanitize=undefined -shared-libsan -Wl,-rpath,/usr/lib/llvm-19/lib/clang/19/lib/linux && ./a.out
I see a libclang_rt.ubsan_standalone-{x86_64,aarch64}-android.so in /system/lib64. Not sure if the platform is affected by this bug or not.
Not sure if the platform is affected by this bug or not.
I tried this in Soong:
cc_binary {
name: "hello",
srcs: ["hello.cpp"],
sanitize: {
undefined: true,
},
}
... and I think(?) it's not enabling vptr. The object file had these args:
-fsanitize=bool,integer-divide-by-zero,object-size,return,returns-nonnull-attribute,shift-exponent,unreachable,vla-bound,memtag-heap -fsanitize-trap=all -fsanitize-minimal-runtime -fno-sanitize-trap=integer,undefined -fno-sanitize-recover=integer,undefined -fno-sanitize=implicit-integer-sign-change -fno-sanitize=unsigned-shift-base
I don't see either -fsanitize=undefined or -fsanitize=vptr. Maybe it's just enabling a subset of the UBSan checks?
The -fsanitize-minimal-runtime flag selects libclang_rt.ubsan_minimal-aarch64-android.so rather than libclang_rt.ubsan_standalone-aarch64-android.so, but I think we link against the latter (standalone) library anyway. I think vptr isn't enabled.
The binary does depend on the shared library:
$ readelf -d /x/main/out/target/product/husky/system/bin/hello
Dynamic section at offset 0x8000 contains 27 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libclang_rt.ubsan_standalone-aarch64-android.so]
0x0000000000000001 (NEEDED) Shared library: [libc++.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so]
0x0000000000000001 (NEEDED) Shared library: [libm.so]
0x0000000000000001 (NEEDED) Shared library: [libdl.so]
...
Anyway, intuitively, the fix is for libclang_rt.ubsan_standalone-aarch64-android.so to depend on libc++[_shared].so. I think we have different NDK vs platform versions of these libraries.
IIRC, the original repro steps here didn't push the UBSan shared object to the device, so instead it used /system/lib64/libclang_rt.ubsan_standalone-aarch64-android.so, so it could start mixing libc++.so and libc++_shared.so -- not sure if that would work.
Is that supposed to be loadable by apps? I know we did that for HWASan but I don't remember UBSan being a part of it.
I don't see the UBSan shared object in /system/etc/public.libraries.txt, so apps shouldn't be able to load it.
Thanks, I couldn't remember the name of the file so couldn't find it with code search.