Hi all,
I've been using GoogleMock & GoogleTest for many years, and have come to love it.
I'm in the process of moving our company's codebase to GCC 5.2 (from 4.9.2). All compiles and runs perfectly, but if I use the latest sanitizers (-fsanitize=address and/or -fsanitize=undefined), things start to go wrong in GMock. We ran fine with GCC 4.9.2 with these flags, but that of course doesn't necessarily prove anything :)
The failures in our code base are complex, so I tried to winnow it to a small repro case. I was only partially able to do so, and so one thing I found I note here
First up, I got the latest code from github, and modified the Makefile in googlemock/make to point at GCC 5.2. All built and ran fine (yay). Then adding -fsanitize=address to CPPFLAGS shows this issue:
----
$ make
/home/mgodbolt/.fighome/runtime/gcc/5.2.0-1/bin/g++ -isystem ../../googletest//include -isystem ../include -Wl,-rpath,/home/mgodbolt/.fighome/runtime/gcc/5.2.0-1/lib64 -fsanitize=address -g -Wall -Wextra -pthread -c ../test/gmock_test.cc
/home/mgodbolt/.fighome/runtime/gcc/5.2.0-1/bin/g++ -isystem ../../googletest//include -isystem ../include -Wl,-rpath,/home/mgodbolt/.fighome/runtime/gcc/5.2.0-1/lib64 -fsanitize=address -I../../googletest/ -I.. -g -Wall -Wextra -pthread \
-c ../src/gmock-all.cc
/home/mgodbolt/.fighome/runtime/gcc/5.2.0-1/bin/g++ -isystem ../../googletest//include -isystem ../include -Wl,-rpath,/home/mgodbolt/.fighome/runtime/gcc/5.2.0-1/lib64 -fsanitize=address -I../../googletest/ -I.. -g -Wall -Wextra -pthread \
-c ../../googletest//src/gtest-all.cc
/home/mgodbolt/.fighome/runtime/gcc/5.2.0-1/bin/g++ -isystem ../../googletest//include -isystem ../include -Wl,-rpath,/home/mgodbolt/.fighome/runtime/gcc/5.2.0-1/lib64 -fsanitize=address -I../../googletest/ -I.. -g -Wall -Wextra -pthread \
-c ../src/gmock_main.cc
ar rv gmock_main.a gmock-all.o gtest-all.o gmock_main.o
ar: creating gmock_main.a
a - gmock-all.o
a - gtest-all.o
a - gmock_main.o
/home/mgodbolt/.fighome/runtime/gcc/5.2.0-1/bin/g++ -isystem ../../googletest//include -isystem ../include -Wl,-rpath,/home/mgodbolt/.fighome/runtime/gcc/5.2.0-1/lib64 -fsanitize=address -g -Wall -Wextra -pthread -lpthread gmock_test.o gmock_main.a -o gmock_test
$ ./gmock_test
Running main() from gmock_main.cc
[==========] Running 11 tests from 3 test cases.
[----------] Global test environment set-up.
[----------] 5 tests from InitGoogleMockTest
[ RUN ] InitGoogleMockTest.ParsesInvalidCommandLine
[ OK ] InitGoogleMockTest.ParsesInvalidCommandLine (0 ms)
[ RUN ] InitGoogleMockTest.ParsesEmptyCommandLine
[ OK ] InitGoogleMockTest.ParsesEmptyCommandLine (0 ms)
[ RUN ] InitGoogleMockTest.ParsesSingleFlag
=================================================================
==58143==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffcd885b578 at pc 0x00000041f1e4 bp 0x7ffcd885b230 sp 0x7ffcd885b228
READ of size 8 at 0x7ffcd885b578 thread T0
#0 0x41f1e3 in void testing::internal::InitGoogleMockImpl<char>(int*, char**) (/home/mgodbolt/dev/googletest/googlemock/make/gmock_test+0x41f1e3)
#1 0x4170da in testing::InitGoogleMock(int*, char**) ../src/gmock.cc:174
#2 0x40a0fd in void TestInitGoogleMock<char, 3, 2>(char const* (&) [3], char const* (&) [2], std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (/home/mgodbolt/dev/googletest/googlemock/make/gmock_test+0x40a0fd)
#3 0x406f5b in InitGoogleMockTest_ParsesSingleFlag_Test::TestBody() ../test/gmock_test.cc:104
#4 0x465730 in void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) ../../googletest/src/gtest.cc:2402
#5 0x45bb47 in void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) ../../googletest/src/gtest.cc:2438
#6 0x435115 in testing::Test::Run() ../../googletest/src/gtest.cc:2475
#7 0x435e3f in testing::TestInfo::Run() ../../googletest/src/gtest.cc:2656
#8 0x436747 in testing::TestCase::Run() ../../googletest/src/gtest.cc:2774
#9 0x440748 in testing::internal::UnitTestImpl::RunAllTests() ../../googletest/src/gtest.cc:4647
#10 0x467a22 in bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) ../../googletest/src/gtest.cc:2402
#11 0x45d22b in bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) ../../googletest/src/gtest.cc:2438
#12 0x43e420 in testing::UnitTest::Run() ../../googletest/src/gtest.cc:4255
#13 0x478937 in RUN_ALL_TESTS() ../../googletest//include/gtest/gtest.h:2237
#14 0x478893 in main ../src/gmock_main.cc:53
#15 0x7f46d3575ec4 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21ec4)
#16 0x406b28 (/home/mgodbolt/dev/googletest/googlemock/make/gmock_test+0x406b28)
Address 0x7ffcd885b578 is located in stack of thread T0 at offset 120 in frame
#0 0x406e65 in InitGoogleMockTest_ParsesSingleFlag_Test::TestBody() ../test/gmock_test.cc:92
This frame has 2 object(s):
[32, 48) 'new_argv'
[96, 120) 'argv' <== Memory access at offset 120 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow ??:0 void testing::internal::InitGoogleMockImpl<char>(int*, char**)
Shadow bytes around the buggy address:
0x10001b103650: 00 00 00 00 f3 f3 f3 f3 00 00 00 00 00 00 00 00
0x10001b103660: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10001b103670: 00 00 00 00 f1 f1 f1 f1 04 f4 f4 f4 f2 f2 f2 f2
0x10001b103680: 00 00 f4 f4 f2 f2 f2 f2 00 00 00 00 f3 f3 f3 f3
0x10001b103690: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x10001b1036a0: f1 f1 f1 f1 00 00 f4 f4 f2 f2 f2 f2 00 00 00[f4]
0x10001b1036b0: f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
0x10001b1036c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10001b1036d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10001b1036e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10001b1036f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Heap right redzone: fb
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
==58143==ABORTING
----
The failure we saw in our codebase (which uses the release 1.7.0 version; *not* the github head, in case that's relevant) seems to be related to -fsanitize=undefined. I've been unable to get a repro case, but I'll describe what I saw in case anyone has any ideas: upon calls to mock methods without expectations, I see a crash in FunctionMockerBase::InvokeWith:
include/gmock/gmock-spec-builders.h:1530:60: runtime error: member call on null pointer of type 'const struct ResultHolder'
ASAN:SIGSEGV
=================================================================
==43330==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x00000067a90c bp 0x7ffc86243550 sp 0x7ffc862434f0 T0)
#0 0x67a90b in testing::internal::FunctionMockerBase<void (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, ExternalIocOms<int>::Order const&, int&)>::InvokeWith(std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, ExternalIocOms<int>::Order const&, int&> const&) include/gmock/gmock-spec-builders.h:1530
#1 0x67a90b in testing::internal::FunctionMocker<void (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, ExternalIocOms<int>::Order const&, int&)>::Invoke(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, ExternalIocOms<int>::Order const&, int&) include/gmock/gmock-generated-function-mockers.h:162
#2 0x67a90b in onNew test/lbm/ExternalIocOmsTest.cpp:21
this crash goes away if I take out the "undefined" sanitizer, which could either point the finger at a GCC sanitizer bug, or perhaps there's a change buried somewhere in the diffs between 1.7.0 and git head that I haven't spotted (lots of things moved around...).
Any ideas or thoughts on either of these issues welcomed.
Much obliged,
Matt