GCC UBSAN visibility=hidden and rtti

71 Views Asked by At

I am facing issues with ubsan and gcc related to visibility which I do not understand. When I look at https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80963 it is stated that due to the visibility the example code is actually undefined behavior. When I try this with different gcc versions on ubuntu [gcc version 13.1.0 (Ubuntu 13.1.0-2ubuntu2~23.04)] and RHEL8 gcc-toolset-13 [gcc version 13.1.1 20230614 (Red Hat 13.1.1-4) (GCC)] the following script

$ cat ubsan_example.sh
#!/bin/bash

set -ex

cat << EOF |
struct Archive {
        virtual void foo() = 0;
};
__attribute__((visibility("default"))) Archive *factory();
EOF
awk '{print}' > lib.h

cat << EOF |
#include "lib.h"
struct ArchiveImpl : Archive { void foo(); };
void ArchiveImpl::foo() {}
Archive *factory() { return new ArchiveImpl; }
EOF
awk '{print}' > libimpl.cpp

cat << EOF |
#include "lib.h"
int main(void) {
        factory()->foo();
}
EOF
awk '{print}' > main.cpp

echo "Compiler information"
g++ -v

g++ -fPIC -o libimpl.so -shared libimpl.cpp -fvisibility=hidden -Wall -fsanitize=undefined -lubsan
g++ -o main main.cpp ./libimpl.so -fvisibility=hidden -Wall -fsanitize=undefined -lubsan
echo "Running main"
./main

g++ -o main-nortti main.cpp ./libimpl.so -fvisibility=hidden -Wall -fsanitize=undefined -fno-rtti -lubsan
echo "Running main-nortti"
./main-nortti

detects undefined behavior only when I run it on RHEL and only when I do not add -fno-rtti.

Output Ubuntu:

$ ./ubsan_example.sh
+ cat
+ awk '{print}'
+ cat
+ awk '{print}'
+ cat
+ awk '{print}'
+ echo 'Compiler information'
Compiler information
+ g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-linux-gnu/13/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 13.1.0-2ubuntu2~23.04' --with-bugurl=file:///usr/share/doc/gcc-13/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2,rust --prefix=/usr --with-gcc-major-version-only --program-suffix=-13 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/libexec --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-13-aYYs71/gcc-13-13.1.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-13-aYYs71/gcc-13-13.1.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 13.1.0 (Ubuntu 13.1.0-2ubuntu2~23.04)
+ g++ -fPIC -o libimpl.so -shared libimpl.cpp -fvisibility=hidden -Wall -fsanitize=undefined -lubsan
+ g++ -o main main.cpp ./libimpl.so -fvisibility=hidden -Wall -fsanitize=undefined -lubsan
+ echo 'Running main'
Running main
+ ./main
+ g++ -o main-nortti main.cpp ./libimpl.so -fvisibility=hidden -Wall -fsanitize=undefined -fno-rtti -lubsan
+ echo 'Running main-nortti'
Running main-nortti
+ ./main-nortti

Output RHEL8:

$ ./ubsan_example.sh
+ cat
+ awk '{print}'
+ cat
+ awk '{print}'
+ cat
+ awk '{print}'
+ echo 'Compiler information'
Compiler information
+ g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/opt/rh/gcc-toolset-13/root/usr/libexec/gcc/x86_64-redhat-linux/13/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-redhat-linux
Configured with: ../configure --enable-bootstrap --enable-languages=c,c++,fortran,lto --prefix=/opt/rh/gcc-toolset-13/root/usr --mandir=/opt/rh/gcc-toolset-13/root/usr/share/man --infodir=/opt/rh/gcc-toolset-13/root/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-gcc-major-version-only --enable-libstdcxx-backtrace --with-libstdcxx-zoneinfo=/opt/rh/gcc-toolset-13/root/usr/share/zoneinfo --with-linker-hash-style=gnu --enable-plugin --enable-initfini-array --with-isl=/builddir/build/BUILD/gcc-13.1.1-20230614/obj-x86_64-redhat-linux/isl-install --enable-offload-targets=nvptx-none --without-cuda-driver --enable-offload-defaulted --enable-gnu-indirect-function --enable-cet --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 13.1.1 20230614 (Red Hat 13.1.1-4) (GCC)
+ g++ -fPIC -o libimpl.so -shared libimpl.cpp -fvisibility=hidden -Wall -fsanitize=undefined -lubsan
+ g++ -o main main.cpp ./libimpl.so -fvisibility=hidden -Wall -fsanitize=undefined -lubsan
+ echo 'Running main'
Running main
+ ./main
main.cpp:3:23: runtime error: member call on address 0x000000550eb0 which does not point to an object of type 'Archive'
0x000000550eb0: note: object is of type 'ArchiveImpl'
 00 00 00 00  80 8d f0 13 b9 7f 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  21 00 00 00
              ^~~~~~~~~~~~~~~~~~~~~~~
              vptr for 'ArchiveImpl'
+ g++ -o main-nortti main.cpp ./libimpl.so -fvisibility=hidden -Wall -fsanitize=undefined -fno-rtti -lubsan
+ echo 'Running main-nortti'
Running main-nortti
+ ./main-nortti

My questions are:

  • Is this actually undefined behavior?
  • Why the runtime error only occurs on the RHEL version of gcc? Can it be some patches or different default flags? Or is it really because of gcc 13.1.0 vs gcc 13.1.1?
  • Why no undefined behavior is detected if I add -fno-rtti when compiling main.cpp?

Here are the code snippets as well (taken from https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80963):

$ cat lib.h

struct Archive {
        virtual void foo() = 0;
};
__attribute__((visibility("default"))) Archive *factory();

$ cat libimpl.cpp

#include "lib.h"
struct ArchiveImpl : Archive { void foo(); };
void ArchiveImpl::foo() {}
Archive *factory() { return new ArchiveImpl; }

$ cat main.cpp

#include "lib.h"
int main(void) {
        factory()->foo();
}

Compile with:

g++ -fPIC -o libimpl.so -shared libimpl.cpp -fvisibility=hidden -Wall -fsanitize=undefined -lubsan
g++ -o main main.cpp ./libimpl.so -fvisibility=hidden -Wall -fsanitize=undefined -lubsan
# disable rtti when compiling main.cpp
g++ -o main-nortti main.cpp ./libimpl.so -fvisibility=hidden -Wall -fsanitize=undefined -fno-rtti -lubsan
0

There are 0 best solutions below