Quuxplusone / LLVMBugzillaTest

0 stars 0 forks source link

invalid symbol type in a shared library with -fvisibility=hidden when using a custom allocator in std::basic_string #20321

Open Quuxplusone opened 10 years ago

Quuxplusone commented 10 years ago
Bugzilla Link PR20322
Status NEW
Importance P normal
Reported by Georgi D (georgid@outlook.com)
Reported on 2014-07-16 10:04:33 -0700
Last modified on 2014-07-24 15:29:38 -0700
Version 3.4
Hardware PC Linux
CC dgregor@apple.com, georgid@outlook.com, llvm-bugs@lists.llvm.org, rafael@espindo.la
Fixed by commit(s)
Attachments src_files.zip (1576 bytes, application/zip)
Blocks
Blocked by
See also
Created attachment 12775
The source files and the build script to reproduce the issue.

I am trying to assign a value to std::basic_string with a custom Allocator
across a shared library boundaries and the code generated causes a crash when
compiling with -fvisibility=hidden.

Here are files used:

% cat SecureAllocator.h
#pragma once

#ifndef SECURE_ALLOCATOR_H_
#define SECURE_ALLOCATOR_H_

#include <memory>
#include <algorithm>

namespace Runtime {

template <class T>
class SecureAllocator : public std::allocator<T> {
   typedef std::allocator<T> base_type;

public:
    template<class U> struct rebind { typedef SecureAllocator<U> other; };

    SecureAllocator() throw() {}
    SecureAllocator(const SecureAllocator&) throw() : base_type() { }
    template <class U>
    SecureAllocator(const SecureAllocator<U>&) throw() : base_type() { }

    void deallocate(typename base_type::pointer p,
                    typename base_type::size_type n) {
        std::fill_n((volatile char*)p, n * sizeof(T), 0);
        base_type::deallocate(p, n);
    }
};

} // namespace Runtime

#endif // SECURE_ALLOCATOR_H_

% cat Value.h
#pragma once

#ifndef VALUE_H_
#define VALUE_H_

#include <string>
#include "SecureAllocator.h"

namespace Runtime {

typedef std::basic_string<char,
                          std::char_traits<char>,
                          SecureAllocator<char> > SecretString;

class Value {
public:
   template<typename T>
   static void Get(T& out);

};

} // namespace Runtime

#endif // VALUE_H_

% cat Value.cpp
#include "Value.h"

namespace Runtime {

template<>
__attribute__ ((visibility ("default"))) void
Value::Get<SecretString>(SecretString& out)
{
   out = SecretString("something");
}

} // namespace Runtime

% cat main.cpp

#include "Value.h"

using namespace Runtime;

int main()
{
   SecretString tmp;
   Value::Get(tmp);
}

The file Value.cpp is build into the libvalue.so and then main.cpp is linked
into an executable against libvalue.so

% cat build-lite.sh
#!/bin/sh

SH=/bin/sh
BLD_DIR=build
CXX=${CXX:=clang++}

CXXFLAGS="-fPIC -fvisibility=hidden -pipe"
LDFLAGS=

echo "CXX = $CXX"
echo "CXXFLAGS = $CXXFLAGS"
echo "LDFLAGS = $LDFLAGS"

mkdir $BLD_DIR

# compile the files
$CXX -c -v -o $BLD_DIR/Value.o $CXXFLAGS Value.cpp
$CXX -c -v -o $BLD_DIR/main.o $CXXFLAGS main.cpp

# link the shared lib
$CXX -v -shared -o $BLD_DIR/libvalue.so $LDFLAGS $BLD_DIR/Value.o

# link the executable
$CXX -o $BLD_DIR/a.out $LDFLAGS $BLD_DIR/main.o $BLD_DIR/libvalue.so

Here is the output from the above script:

% ./build-lite.sh
CXX = clang++
CXXFLAGS = -fPIC -fvisibility=hidden -pipe
LDFLAGS =

clang version 3.4 (http://llvm.org/git/clang
48eff6c3512fd6c768072b05ab4c287c7719072b) (http://llvm.org/git/llvm
8240ef04108620fef51219e9495a6e71e95ccd75)
Target: x86_64-unknown-linux-gnu
Thread model: posix
 "/dbc/dbc101/georgid/sysroot/bin/clang" -cc1 -triple x86_64-unknown-linux-gnu -emit-obj -mrelax-all -disable-free -main-file-name Value.cpp -mrelocation-model pic -pic-level 2 -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-linker-version 2.20.51.0.2 -v -coverage-file /dbc/dbc101/georgid/tmp/clang_bugs/clang-secure-alloc/runtime/build/Value.o -resource-dir /dbc/dbc101/georgid/sysroot/bin/../lib/clang/3.4 -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7 -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7/x86_64-redhat-linux -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7/backward -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/x86_64-redhat-linux/c++/4.4.7 -internal-isystem /usr/local/include -internal-isystem /dbc/dbc101/georgid/sysroot/bin/../lib/clang/3.4/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdeprecated-macro -fdebug-compilation-dir /dbc/dbc101/georgid/tmp/clang_bugs/clang-secure-alloc/runtime -ferror-limit 19 -fmessage-length 229 -fvisibility hidden -mstackrealign -fobjc-runtime=gcc -fobjc-default-synthesize-properties -fcxx-exceptions -fexceptions -fdiagnostics-show-option -fcolor-diagnostics -backend-option -vectorize-loops -o build/Value.o -x c++ Value.cpp
clang -cc1 version 3.4 based upon LLVM 3.4svn default target x86_64-unknown-
linux-gnu
ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-
linux/4.4.7/../../../../include/x86_64-redhat-linux/c++/4.4.7"
ignoring nonexistent directory "/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7
 /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7/x86_64-redhat-linux
 /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7/backward
 /usr/local/include
 /dbc/dbc101/georgid/sysroot/bin/../lib/clang/3.4/include
 /usr/include
End of search list.
clang version 3.4 (http://llvm.org/git/clang
48eff6c3512fd6c768072b05ab4c287c7719072b) (http://llvm.org/git/llvm
8240ef04108620fef51219e9495a6e71e95ccd75)
Target: x86_64-unknown-linux-gnu
Thread model: posix
 "/dbc/dbc101/georgid/sysroot/bin/clang" -cc1 -triple x86_64-unknown-linux-gnu -emit-obj -mrelax-all -disable-free -main-file-name main.cpp -mrelocation-model pic -pic-level 2 -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-linker-version 2.20.51.0.2 -v -coverage-file /dbc/dbc101/georgid/tmp/clang_bugs/clang-secure-alloc/runtime/build/main.o -resource-dir /dbc/dbc101/georgid/sysroot/bin/../lib/clang/3.4 -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7 -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7/x86_64-redhat-linux -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7/backward -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/x86_64-redhat-linux/c++/4.4.7 -internal-isystem /usr/local/include -internal-isystem /dbc/dbc101/georgid/sysroot/bin/../lib/clang/3.4/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdeprecated-macro -fdebug-compilation-dir /dbc/dbc101/georgid/tmp/clang_bugs/clang-secure-alloc/runtime -ferror-limit 19 -fmessage-length 229 -fvisibility hidden -mstackrealign -fobjc-runtime=gcc -fobjc-default-synthesize-properties -fcxx-exceptions -fexceptions -fdiagnostics-show-option -fcolor-diagnostics -backend-option -vectorize-loops -o build/main.o -x c++ main.cpp
clang -cc1 version 3.4 based upon LLVM 3.4svn default target x86_64-unknown-
linux-gnu
ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-
linux/4.4.7/../../../../include/x86_64-redhat-linux/c++/4.4.7"
ignoring nonexistent directory "/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7
 /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7/x86_64-redhat-linux
 /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7/backward
 /usr/local/include
 /dbc/dbc101/georgid/sysroot/bin/../lib/clang/3.4/include
 /usr/include
End of search list.
clang version 3.4 (http://llvm.org/git/clang
48eff6c3512fd6c768072b05ab4c287c7719072b) (http://llvm.org/git/llvm
8240ef04108620fef51219e9495a6e71e95ccd75)
Target: x86_64-unknown-linux-gnu
Thread model: posix
 "/usr/bin/ld" --eh-frame-hdr -m elf_x86_64 -shared -o build/libvalue.so /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.4.7/crtbeginS.o -L/usr/lib/gcc/x86_64-redhat-linux/4.4.7 -L/usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../.. -L/lib -L/usr/lib build/Value.o -lstdc++ -lm -lgcc_s -lc -lgcc_s /usr/lib/gcc/x86_64-redhat-linux/4.4.7/crtendS.o /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64/crtn.o

When I try to run the above binary I get:

% ./build/a.out
*** glibc detected *** ./build/a.out: free(): invalid pointer:
0x0000000000601c40 ***
======= Backtrace: =========
/lib64/libc.so.6[0x38a1276126]
./build/a.out(_ZN9__gnu_cxx13new_allocatorIcE10deallocateEPcm+0x1d)[0x40104d]
build/libvalue.so(+0x194b)[0x7feb11d1d94b]
build/libvalue.so(+0x1884)[0x7feb11d1d884]
build/libvalue.so(+0x17da)[0x7feb11d1d7da]
build/libvalue.so(+0x1615)[0x7feb11d1d615]
build/libvalue.so(+0x149d)[0x7feb11d1d49d]
build/libvalue.so(_ZN7Runtime5Value3GetISbIcSt11char_traitsIcENS_15SecureAllocatorIcEEEEEvRT_+0x46)[0x7feb11d1d2b6]
./build/a.out[0x400b11]
/lib64/libc.so.6(__libc_start_main+0xfd)[0x38a121ecdd]
./build/a.out[0x400a29]
======= Memory map: ========
00400000-00402000 r-xp 00000000 fd:00 322571941
/dbc/dbc101/georgid/tmp/clang_bugs/clang-secure-alloc/runtime/build/a.out
00601000-00602000 rw-p 00001000 fd:00 322571941
/dbc/dbc101/georgid/tmp/clang_bugs/clang-secure-alloc/runtime/build/a.out
00e1e000-00e3f000 rw-p 00000000 00:00 0                                  [heap]
312a800000-312a8e8000 r-xp 00000000 08:03 26478803
/usr/lib64/libstdc++.so.6.0.13
312a8e8000-312aae8000 ---p 000e8000 08:03 26478803
/usr/lib64/libstdc++.so.6.0.13
312aae8000-312aaef000 r--p 000e8000 08:03 26478803
/usr/lib64/libstdc++.so.6.0.13
312aaef000-312aaf1000 rw-p 000ef000 08:03 26478803
/usr/lib64/libstdc++.so.6.0.13
312aaf1000-312ab06000 rw-p 00000000 00:00 0
3728800000-3728816000 r-xp 00000000 08:03 524495
/lib64/libgcc_s-4.4.7-20120601.so.1
3728816000-3728a15000 ---p 00016000 08:03 524495
/lib64/libgcc_s-4.4.7-20120601.so.1
3728a15000-3728a16000 rw-p 00015000 08:03 524495
/lib64/libgcc_s-4.4.7-20120601.so.1
38a0a00000-38a0a20000 r-xp 00000000 08:03 524290
/lib64/ld-2.12.so
38a0c1f000-38a0c20000 r--p 0001f000 08:03 524290
/lib64/ld-2.12.so
38a0c20000-38a0c21000 rw-p 00020000 08:03 524290
/lib64/ld-2.12.so
38a0c21000-38a0c22000 rw-p 00000000 00:00 0
38a1200000-38a138a000 r-xp 00000000 08:03 524295
/lib64/libc-2.12.so
38a138a000-38a1589000 ---p 0018a000 08:03 524295
/lib64/libc-2.12.so
38a1589000-38a158d000 r--p 00189000 08:03 524295
/lib64/libc-2.12.so
38a158d000-38a158e000 rw-p 0018d000 08:03 524295
/lib64/libc-2.12.so
38a158e000-38a1593000 rw-p 00000000 00:00 0
38a1e00000-38a1e83000 r-xp 00000000 08:03 524301
/lib64/libm-2.12.so
38a1e83000-38a2082000 ---p 00083000 08:03 524301
/lib64/libm-2.12.so
38a2082000-38a2083000 r--p 00082000 08:03 524301
/lib64/libm-2.12.so
38a2083000-38a2084000 rw-p 00083000 08:03 524301
/lib64/libm-2.12.so
7feb11d07000-7feb11d0d000 rw-p 00000000 00:00 0
7feb11d1b000-7feb11d1c000 rw-p 00000000 00:00 0
7feb11d1c000-7feb11d20000 r-xp 00000000 fd:00 322571940
/dbc/dbc101/georgid/tmp/clang_bugs/clang-secure-alloc/runtime/build/libvalue.so
7feb11d20000-7feb11f1f000 ---p 00004000 fd:00 322571940
/dbc/dbc101/georgid/tmp/clang_bugs/clang-secure-alloc/runtime/build/libvalue.so
7feb11f1f000-7feb11f20000 rw-p 00003000 fd:00 322571940
/dbc/dbc101/georgid/tmp/clang_bugs/clang-secure-alloc/runtime/build/libvalue.so
7feb11f20000-7feb11f21000 rw-p 00000000 00:00 0
7fff0964b000-7fff09661000 rw-p 00000000 00:00 0                          [stack]
7fff097ff000-7fff09800000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0
[vsyscall]
[3]    64804 abort (core dumped)  ./build/a.out

In GDB the backtrace looks like this:

(gdb) bt
#0  0x00000038a12328e5 in raise (sig=6) at
../nptl/sysdeps/unix/sysv/linux/raise.c:64
#1  0x00000038a12340c5 in abort () at abort.c:92
#2  0x00000038a12707f7 in __libc_message (do_abort=2, fmt=0x38a1357fc0 "***
glibc detected *** %s: %s: 0x%s ***\n") at
../sysdeps/unix/sysv/linux/libc_fatal.c:198
#3  0x00000038a1276126 in malloc_printerr (action=3, str=0x38a13560af "free():
invalid pointer", ptr=<value optimized out>) at malloc.c:6311
#4  0x000000000040104d in __gnu_cxx::new_allocator<char>::deallocate(char*,
unsigned long) ()
#5  0x00007ffff7dfa94b in Runtime::SecureAllocator<char>::deallocate(char*,
unsigned long) () from build/libvalue.so
#6  0x00007ffff7dfa884 in std::basic_string<char, std::char_traits<char>,
Runtime::SecureAllocator<char>
>::_Rep::_M_destroy(Runtime::SecureAllocator<char> const&) () from
build/libvalue.so
#7  0x00007ffff7dfa7da in std::basic_string<char, std::char_traits<char>,
Runtime::SecureAllocator<char>
>::_Rep::_M_dispose(Runtime::SecureAllocator<char> const&) () from
build/libvalue.so
#8  0x00007ffff7dfa615 in std::basic_string<char, std::char_traits<char>,
Runtime::SecureAllocator<char> >::assign(std::basic_string<char,
std::char_traits<char>, Runtime::SecureAllocator<char> > const&) () from
build/libvalue.so
#9  0x00007ffff7dfa49d in std::basic_string<char, std::char_traits<char>,
Runtime::SecureAllocator<char> >::operator=(std::basic_string<char,
std::char_traits<char>, Runtime::SecureAllocator<char> > const&) () from
build/libvalue.so
#10 0x00007ffff7dfa2b6 in void Runtime::Value::Get<std::basic_string<char,
std::char_traits<char>, Runtime::SecureAllocator<char> >
>(std::basic_string<char, std::char_traits<char>,
Runtime::SecureAllocator<char> >&) () from build/libvalue.so
#11 0x0000000000400b11 in main ()

The issue is that the code attempts to delete the buffer used for empty string
which should not happen.

The exact same code works fine which clang 3.2 and gcc 4.4.3 and above (I have
not tested with older versions)

It is also working with clang 3.4 when -fvisibility=hidden is not set.

As far as I can see the problem is with the

std::basic_string<char, std::char_traits<char>, Runtime::SecureAllocator<char>
>::_Rep::_S_empty_rep_storage symbol which is used for the "empty string" case.

The code generated by gcc is: (Working)

% nm -a -C build_gcc/a.out |grep empty
0000000000401310 W std::basic_string<char, std::char_traits<char>,
Runtime::SecureAllocator<char> >::_S_empty_rep()
0000000000401426 W std::basic_string<char, std::char_traits<char>,
Runtime::SecureAllocator<char> >::_Rep::_S_empty_rep()
0000000000602040 u std::basic_string<char, std::char_traits<char>,
Runtime::SecureAllocator<char> >::_Rep::_S_empty_rep_storage

% nm -a -C build_gcc/libvalue.so |grep empty
0000000000002fa6 W std::basic_string<char, std::char_traits<char>,
Runtime::SecureAllocator<char> >::_S_empty_rep()
0000000000002b71 W std::basic_string<char, std::char_traits<char>,
Runtime::SecureAllocator<char> >::_Rep::_S_empty_rep()
0000000000205040 u std::basic_string<char, std::char_traits<char>,
Runtime::SecureAllocator<char> >::_Rep::_S_empty_rep_storage

The code generated by Clang 3.2 is:  (Working)

% nm -a -C build_clang32/a.out |grep empty
0000000000401820 W std::basic_string<char, std::char_traits<char>,
Runtime::SecureAllocator<char> >::_S_empty_rep()
0000000000401560 W std::basic_string<char, std::char_traits<char>,
Runtime::SecureAllocator<char> >::_Rep::_S_empty_rep()
0000000000603020 V std::basic_string<char, std::char_traits<char>,
Runtime::SecureAllocator<char> >::_Rep::_S_empty_rep_storage

% nm -a -C build_clang32/libvalue.so |grep empty
00000000000037e0 W std::basic_string<char, std::char_traits<char>,
Runtime::SecureAllocator<char> >::_S_empty_rep()
0000000000002c10 W std::basic_string<char, std::char_traits<char>,
Runtime::SecureAllocator<char> >::_Rep::_S_empty_rep()
0000000000205020 V std::basic_string<char, std::char_traits<char>,
Runtime::SecureAllocator<char> >::_Rep::_S_empty_rep_storage

The code generated by Clang 3.4 is: (Crash)

% nm -a -C build_clang34/a.out |grep empty
00000000004011c0 W std::basic_string<char, std::char_traits<char>,
Runtime::SecureAllocator<char> >::_S_empty_rep()
0000000000400f00 W std::basic_string<char, std::char_traits<char>,
Runtime::SecureAllocator<char> >::_Rep::_S_empty_rep()
0000000000602020 V std::basic_string<char, std::char_traits<char>,
Runtime::SecureAllocator<char> >::_Rep::_S_empty_rep_storage

% nm -a -C build_clang34/libvalue.so |grep empty
0000000000002430 t std::basic_string<char, std::char_traits<char>,
Runtime::SecureAllocator<char> >::_S_empty_rep()
0000000000001840 t std::basic_string<char, std::char_traits<char>,
Runtime::SecureAllocator<char> >::_Rep::_S_empty_rep()
0000000000204020 b std::basic_string<char, std::char_traits<char>,
Runtime::SecureAllocator<char> >::_Rep::_S_empty_rep_storage
Quuxplusone commented 10 years ago

Attached src_files.zip (1576 bytes, application/zip): The source files and the build script to reproduce the issue.