Googletest: Undefined behavior "member call on null pointer of type 'const struct ResultHolder'"

Created on 3 Feb 2016  路  3Comments  路  Source: google/googletest

Compiling with gcc-6 leads to segfaults in projects that use googletest,
because it invokes undefined behavior.

For example the yaml-cpp testsuite segfaults:
/var/tmp/yaml-cpp/test/gmock-1.7.0/include/gmock/gmock-spec-builders.h:1530:60: runtime error: member call on null pointer of type 'const struct ResultHolder'
[1] 20019 segmentation fault ./run-tests

"this" must never be NULL in C++.
Using "-fno-delete-null-pointer-checks" is a workaround.

1579   // Returns the result of invoking this mock function with the given                                                                                                    
1580   // arguments.  This function can be safely called from multiple                                                                                                        
1581   // threads concurrently.                                                                                                                                               
1582   Result InvokeWith(const ArgumentTuple& args)                                                                                                                           
1583         GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {                                                                                                                            
1584     scoped_ptr<ResultHolder> holder(                                                                                                                                     
1585         DownCast_<ResultHolder*>(this->UntypedInvokeWith(&args)));                                                                                                       
1586     return holder->Unwrap();                                                                                                                                             
1587   }  

Most helpful comment

Hi,

I also have the same issue with some valid unit test when using GCC 6.

Actually, this issue was fixed back in 2014, but the lack of GTest/GMock release since 2013 make us still hitting this issue... It was fixed in the trunk with b5c81098. This bug is only triggered when you try to mock a method returning void.

For those who don't want to use the trunk without a proper release, here is a patch that applies fine against GMock 1.7.0 which fixes this issue (without using -fno-delete-null-pointer-checks):

--- include/gmock/gmock-spec-builders.h
+++ include/gmock/gmock-spec-builders.h
@@ -1370,6 +1370,8 @@
 template <>
 class ActionResultHolder<void> : public UntypedActionResultHolderBase {
  public:
+  explicit ActionResultHolder() {}
+
   void GetValueAndDelete() const { delete this; }

   virtual void PrintAsActionResult(::std::ostream* /* os */) const {}
@@ -1381,7 +1383,7 @@
       const typename Function<F>::ArgumentTuple& args,
       const string& call_description) {
     func_mocker->PerformDefaultAction(args, call_description);
-    return NULL;
+    return new ActionResultHolder();
   }

   // Performs the given action and returns NULL.
@@ -1390,7 +1392,7 @@
       const Action<F>& action,
       const typename Function<F>::ArgumentTuple& args) {
     action.Perform(args);
-    return NULL;
+    return new ActionResultHolder();
   }
 };

Cheers,
Romain

All 3 comments

That snippet doesn't help me much. What ptr was null there?

Do you have more context for how a nullptr had a member function called on it?

markus@x4 test % gdb ./run-tests
Reading symbols from ./run-tests...done.
(gdb) b __ubsan::Diag::~Diag
(gdb) run
...
Breakpoint 1, 0x00007ffff6dfec40 in __ubsan::Diag::~Diag() () from /usr/lib/gcc/x86_64-pc-linux-gnu/6.0.0/libubsan.so.0
(gdb) bt
#0  0x00007ffff6dfec40 in __ubsan::Diag::~Diag() () from /usr/lib/gcc/x86_64-pc-linux-gnu/6.0.0/libubsan.so.0
#1  0x00007ffff6e02241 in handleTypeMismatchImpl(__ubsan::TypeMismatchData*, unsigned long, __ubsan::ReportOptions) ()
   from /usr/lib/gcc/x86_64-pc-linux-gnu/6.0.0/libubsan.so.0
#2  0x00007ffff6e026d3 in __ubsan_handle_type_mismatch () from /usr/lib/gcc/x86_64-pc-linux-gnu/6.0.0/libubsan.so.0
#3  0x00000000005003dc in testing::internal::FunctionMockerBase<void (YAML::Mark const&)>::InvokeWith(std::tuple<YAML::Mark const&> const&) (
    args=std::tuple containing = {...}, this=0x2842350) at /var/tmp/yaml-cpp/test/gmock-1.7.0/include/gmock/gmock-spec-builders.h:1530
#4  testing::internal::FunctionMocker<void (YAML::Mark const&)>::Invoke(YAML::Mark const&) (a1=..., this=0x2842350)
    at /var/tmp/yaml-cpp/test/gmock-1.7.0/include/gmock/gmock-generated-function-mockers.h:97
#5  YAML::MockEventHandler::OnDocumentStart (this=<optimized out>, gmock_a1=...) at /var/tmp/yaml-cpp/test/mock_event_handler.h:10
#6  0x0000000001320f0f in YAML::SingleDocParser::HandleDocument (this=0x7fffffffda10, eventHandler=...) at /var/tmp/yaml-cpp/src/singledocparser.cpp:31
#7  0x00000000012686f5 in YAML::Parser::HandleNextDocument (this=this@entry=0x7fffffffdc50, eventHandler=...) at /var/tmp/yaml-cpp/src/parser.cpp:42
#8  0x00000000004f9717 in YAML::HandlerTest::Parse (
    example="- |\n  !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\n- |\n  隆垄拢陇楼娄搂篓漏陋芦卢颅庐炉掳卤虏鲁麓碌露路赂鹿潞禄录陆戮驴脌脕脗脙脛脜脝脟脠脡脢脣脤脥", <
incomplete sequence \303>..., this=0x2842330) at /var/tmp/yaml-cpp/test/handler_test.h:17
#9  YAML::(anonymous namespace)::EncodingTest::Run (this=0x2842330) at /var/tmp/yaml-cpp/test/integration/encoding_test.cpp:105
#10 0x000000000145896b in testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void> (location=0x14b5616 "the test body", method=<optimized out>, 
    object=0x2842330) at /var/tmp/yaml-cpp/test/gmock-1.7.0/gtest/src/gtest.cc:2078
#11 testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void> (object=object@entry=0x2842330, method=<optimized out>, 
    location=location@entry=0x14b5616 "the test body") at /var/tmp/yaml-cpp/test/gmock-1.7.0/gtest/src/gtest.cc:2114
#12 0x00000000014226da in testing::Test::Run (this=this@entry=0x2842330) at /var/tmp/yaml-cpp/test/gmock-1.7.0/gtest/src/gtest.cc:2151
#13 0x00000000014231a3 in testing::Test::Run (this=0x2842330) at /var/tmp/yaml-cpp/test/gmock-1.7.0/gtest/src/gtest.cc:2142
#14 testing::TestInfo::Run (this=0x27fc3d0) at /var/tmp/yaml-cpp/test/gmock-1.7.0/gtest/src/gtest.cc:2326
#15 0x0000000001423de8 in testing::TestCase::Run (this=0x27fc4c0) at /var/tmp/yaml-cpp/test/gmock-1.7.0/gtest/src/gtest.cc:2444
#16 0x00000000014256ca in testing::internal::UnitTestImpl::RunAllTests (this=0x27f4d70) at /var/tmp/yaml-cpp/test/gmock-1.7.0/gtest/src/gtest.cc:4315
#17 0x0000000001459bcb in testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool> (
    location=0x14ab870 "auxiliary test code (environments or event listeners)", method=<optimized out>, object=0x1424960 <testing::internal::UnitTestImpl::RunAllTests()>)
    at /var/tmp/yaml-cpp/test/gmock-1.7.0/gtest/src/gtest.cc:2078
#18 testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool> (object=object@entry=0x27f4d70, method=<optimized out>, 
    location=location@entry=0x14ab870 "auxiliary test code (environments or event listeners)") at /var/tmp/yaml-cpp/test/gmock-1.7.0/gtest/src/gtest.cc:2114
#19 0x0000000001427519 in testing::UnitTest::Run (this=0x27e1a00 <testing::UnitTest::GetInstance()::instance>) at /var/tmp/yaml-cpp/test/gmock-1.7.0/gtest/src/gtest.cc:3926
#20 0x0000000000455f08 in RUN_ALL_TESTS () at /var/tmp/yaml-cpp/test/gmock-1.7.0/gtest/include/gtest/gtest.h:2288
#21 main (argc=<optimized out>, argv=<optimized out>) at /var/tmp/yaml-cpp/test/main.cpp:5
(gdb) up
#1  0x00007ffff6e02241 in handleTypeMismatchImpl(__ubsan::TypeMismatchData*, unsigned long, __ubsan::ReportOptions) ()
   from /usr/lib/gcc/x86_64-pc-linux-gnu/6.0.0/libubsan.so.0
(gdb) up
#2  0x00007ffff6e026d3 in __ubsan_handle_type_mismatch () from /usr/lib/gcc/x86_64-pc-linux-gnu/6.0.0/libubsan.so.0
(gdb) up
#3  0x00000000005003dc in testing::internal::FunctionMockerBase<void (YAML::Mark const&)>::InvokeWith(std::tuple<YAML::Mark const&> const&) (
    args=std::tuple containing = {...}, this=0x2842350) at /var/tmp/yaml-cpp/test/gmock-1.7.0/include/gmock/gmock-spec-builders.h:1530
1530            this->UntypedInvokeWith(&args))->GetValueAndDelete();
(gdb) l
1525      // arguments.  This function can be safely called from multiple
1526      // threads concurrently.
1527      Result InvokeWith(const ArgumentTuple& args)
1528            GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
1529        return static_cast<const ResultHolder*>(
1530            this->UntypedInvokeWith(&args))->GetValueAndDelete();
1531      }
1532
1533      // Adds and returns a default action spec for this mock function.
1534      OnCallSpec<F>& AddNewOnCallSpec(
(gdb) p this
testing::internal::FunctionMockerBase<void(const YAML::Mark&)> * const) 0x2842350
(gdb) p *this
{
  <testing::internal::UntypedFunctionMockerBase> = {
    _vptr.UntypedFunctionMockerBase = 0x14c05a8 <vtable for testing::internal::FunctionMocker<void (YAML::Mark const&)>+16>, 
    mock_obj_ = 0x2842348, 
    name_ = 0x14ad1c7 "OnDocumentStart", 
    untyped_on_call_specs_ = std::vector of length 0, capacity 0, 
    untyped_expectations_ = std::vector of length 1, capacity 1 = {{
        value_ = 0x283e390, 
        link_ = {
          next_ = 0x283f588
        }
      }}
  }, 
  members of testing::internal::FunctionMockerBase<void(const YAML::Mark&)>: 
  current_spec_ = {
    function_mocker_ = 0x2842350, 
    matchers_ = std::tuple containing = {
      [1] = {
        <testing::internal::MatcherBase<YAML::Mark const&>> = {
          _vptr.MatcherBase = 0x14c0538 <vtable for testing::Matcher<YAML::Mark const&>+16>, 
          impl_ = {
            value_ = 0x283d040, 
            link_ = {
              next_ = 0x283e490
            }
          }
        }, <No data fields>}
    }
  }
}
(gdb) p this->UntypedInvokeWith(&args)
$5 = (const testing::internal::UntypedActionResultHolderBase *) 0x0

Hi,

I also have the same issue with some valid unit test when using GCC 6.

Actually, this issue was fixed back in 2014, but the lack of GTest/GMock release since 2013 make us still hitting this issue... It was fixed in the trunk with b5c81098. This bug is only triggered when you try to mock a method returning void.

For those who don't want to use the trunk without a proper release, here is a patch that applies fine against GMock 1.7.0 which fixes this issue (without using -fno-delete-null-pointer-checks):

--- include/gmock/gmock-spec-builders.h
+++ include/gmock/gmock-spec-builders.h
@@ -1370,6 +1370,8 @@
 template <>
 class ActionResultHolder<void> : public UntypedActionResultHolderBase {
  public:
+  explicit ActionResultHolder() {}
+
   void GetValueAndDelete() const { delete this; }

   virtual void PrintAsActionResult(::std::ostream* /* os */) const {}
@@ -1381,7 +1383,7 @@
       const typename Function<F>::ArgumentTuple& args,
       const string& call_description) {
     func_mocker->PerformDefaultAction(args, call_description);
-    return NULL;
+    return new ActionResultHolder();
   }

   // Performs the given action and returns NULL.
@@ -1390,7 +1392,7 @@
       const Action<F>& action,
       const typename Function<F>::ArgumentTuple& args) {
     action.Perform(args);
-    return NULL;
+    return new ActionResultHolder();
   }
 };

Cheers,
Romain

Was this page helpful?
0 / 5 - 0 ratings

Related issues

GoogleCodeExporter picture GoogleCodeExporter  路  5Comments

tschijnmo picture tschijnmo  路  4Comments

cyberdecker picture cyberdecker  路  3Comments

robindegen picture robindegen  路  4Comments

nholthaus picture nholthaus  路  6Comments