Search code examples
c++valgrindgoogletest

Valgrind reports usage of uninitialized value(s) on simple gtest


I have following test in foo.cpp which I build with g++ ./foo.cpp -lgtest -lgtest_main -lpthread -O0 -ggdb:

#include "gtest/gtest.h"

struct s {};

class FooTest : public testing::TestWithParam<s> {};

TEST_P(FooTest, DoesBlah) {}

INSTANTIATE_TEST_SUITE_P(MeenyMinyMoe, FooTest, testing::Values(s()));

When I run it with valgrind ./a.out, valgrind reports a bunch of errors like the following:

==33778== Conditional jump or move depends on uninitialised value(s)
==33778==    at 0x4C317CC: _itoa_word (_itoa.c:180)
==33778==    by 0x4C4D6F4: __vfprintf_internal (vfprintf-internal.c:1687)
==33778==    by 0x4C62119: __vsnprintf_internal (vsnprintf.c:114)
==33778==    by 0x4C37F75: snprintf (snprintf.c:31)
==33778==    by 0x14200A: testing::(anonymous namespace)::PrintByteSegmentInObjectTo(unsigned char const*, unsigned long, unsigned long, std::ostream*) (in /home/scandit/private/a.out)
==33778==    by 0x1420AB: testing::(anonymous namespace)::PrintBytesInObjectToImpl(unsigned char const*, unsigned long, std::ostream*) (in /home/scandit/private/a.out)
==33778==    by 0x142151: testing::internal::PrintBytesInObjectTo(unsigned char const*, unsigned long, std::ostream*) (in /home/scandit/private/a.out)
==33778==    by 0x11C519: void testing::internal::RawBytesPrinter::PrintValue<s, 1ul>(s const&, std::ostream*) (gtest-printers.h:268)
==33778==    by 0x11C3C2: void testing::internal::PrintWithFallback<s>(s const&, std::ostream*) (gtest-printers.h:310)
==33778==    by 0x11C158: void testing::internal::PrintTo<s>(s const&, std::ostream*) (gtest-printers.h:439)
==33778==    by 0x11BF2E: testing::internal::UniversalPrinter<s>::Print(s const&, std::ostream*) (gtest-printers.h:701)
==33778==    by 0x11BB5B: void testing::internal::UniversalPrint<s>(s const&, std::ostream*) (gtest-printers.h:998)

When I use e.g. std::string instead of struct s in TestWithParam<...>, then I don't see any errors reported by valgrind. What is missing in my struct ?

I am using g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0, valgrind-3.15.0 and gtest 1.11.0.

Update: Yes, I did try with a standard constructor, without any difference. I run now valgrind --track-origins=yes --num-callers=100 ./a.out.

#include "gtest/gtest.h"

struct s {
    s() {};
};

class FooTest : public testing::TestWithParam<s> {};

TEST_P(FooTest, DoesBlah) {}

INSTANTIATE_TEST_SUITE_P(MeenyMinyMoe, FooTest, testing::Values(s()));
==60500== Conditional jump or move depends on uninitialised value(s)
==60500==    at 0x4C317CC: _itoa_word (_itoa.c:180)
==60500==    by 0x4C4D6F4: __vfprintf_internal (vfprintf-internal.c:1687)
==60500==    by 0x4C62119: __vsnprintf_internal (vsnprintf.c:114)
==60500==    by 0x4C37F75: snprintf (snprintf.c:31)
==60500==    by 0x141868: testing::(anonymous namespace)::PrintByteSegmentInObjectTo(unsigned char const*, unsigned long, unsigned long, std::ostream*) (in /home/user/private/a.out)
==60500==    by 0x141909: testing::(anonymous namespace)::PrintBytesInObjectToImpl(unsigned char const*, unsigned long, std::ostream*) (in /home/user/private/a.out)
==60500==    by 0x1419AF: testing::internal::PrintBytesInObjectTo(unsigned char const*, unsigned long, std::ostream*) (in /home/user/private/a.out)
==60500==    by 0x11C407: void testing::internal::RawBytesPrinter::PrintValue<s, 1ul>(s const&, std::ostream*) (gtest-printers.h:270)
==60500==    by 0x11C2B0: void testing::internal::PrintWithFallback<s>(s const&, std::ostream*) (gtest-printers.h:312)
==60500==    by 0x11C046: void testing::internal::PrintTo<s>(s const&, std::ostream*) (gtest-printers.h:441)
==60500==    by 0x11BE1C: testing::internal::UniversalPrinter<s>::Print(s const&, std::ostream*) (gtest-printers.h:691)
==60500==    by 0x11BA49: void testing::internal::UniversalPrint<s>(s const&, std::ostream*) (gtest-printers.h:980)
==60500==    by 0x11B240: testing::internal::UniversalTersePrinter<s>::Print(s const&, std::ostream*) (gtest-printers.h:865)
==60500==    by 0x11A861: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > testing::PrintToString<s>(s const&) (gtest-printers.h:1018)
==60500==    by 0x119E9E: testing::internal::ParameterizedTestSuiteInfo<FooTest>::RegisterTests() (gtest-param-util.h:590)
==60500==    by 0x147534: testing::internal::ParameterizedTestSuiteRegistry::RegisterTests() (in /home/user/private/a.out)
==60500==    by 0x126425: testing::internal::UnitTestImpl::RegisterParameterizedTests() (in /home/user/private/a.out)
==60500==    by 0x135FD7: testing::internal::UnitTestImpl::PostFlagParsingInit() (in /home/user/private/a.out)
==60500==    by 0x153A81: void testing::internal::InitGoogleTestImpl<char>(int*, char**) (in /home/user/private/a.out)
==60500==    by 0x138B27: testing::InitGoogleTest(int*, char**) (in /home/user/private/a.out)
==60500==    by 0x16CFA6: main (in /home/user/private/a.out)
==60500==  Uninitialised value was created by a heap allocation
==60500==    at 0x483BE63: operator new(unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==60500==    by 0x11C74A: testing::internal::ValuesInIteratorRangeGenerator<s>::Iterator::Current() const (gtest-param-util.h:334)
==60500==    by 0x11A5C4: testing::internal::ParamIterator<s>::operator*() const (gtest-param-util.h:137)
==60500==    by 0x119B18: testing::internal::ParameterizedTestSuiteInfo<FooTest>::RegisterTests() (gtest-param-util.h:572)
==60500==    by 0x147534: testing::internal::ParameterizedTestSuiteRegistry::RegisterTests() (in /home/user/private/a.out)
==60500==    by 0x126425: testing::internal::UnitTestImpl::RegisterParameterizedTests() (in /home/user/private/a.out)
==60500==    by 0x135FD7: testing::internal::UnitTestImpl::PostFlagParsingInit() (in /home/user/private/a.out)
==60500==    by 0x153A81: void testing::internal::InitGoogleTestImpl<char>(int*, char**) (in /home/user/private/a.out)
==60500==    by 0x138B27: testing::InitGoogleTest(int*, char**) (in /home/user/private/a.out)
==60500==    by 0x16CFA6: main (in /home/user/private/a.out)

Solution

  • There's nothing wrong with your structure. The errors come from C++ not initializing the padding byte (sizeof(struct s) == 1), so valgrind duly reports that particular byte wasn't initialized (because it really wasn't). The C++ standard doesn't mandate zero-initialisation

    Quoting cppreference.com Zero-initialization:

    Note that this is not the syntax for zero-initialization, which does not have a dedicated syntax in the language. These are examples of other types of initializations, which might perform zero-initialization.

    I've taken your example

    #include "gtest/gtest.h"
    
    struct s {};
    
    class FooTest : public testing::TestWithParam<s> {};
    
    TEST_P(FooTest, DoesBlah) {}
    
    INSTANTIATE_TEST_SUITE_P(MeenyMinyMoe, FooTest, testing::Values(s()));
    

    I've tried using the -ftrivial-auto-var-init=zero flag (from here), but that doesn't work because Google Test initialises using new (so not an automatic variable). So much for the easy fixes.

    The solution I found is to overload the new and new[] operators:

    #include <iostream>
    
    #include "gtest/gtest.h"
    
    struct s {
    
        static void* operator new(std::size_t count)
        {
            std::cout << "custom new for size " << count << '\n';
            void *p = ::operator new(count);
            memset(p, 0, count);
            return p;
        }
     
        static void* operator new[](std::size_t count)
        {
            std::cout << "custom new[] for size " << count << '\n';
    
            void *p = ::operator new[](count);
            memset(p, 0, count);
            return p;
        }
    };
    
    class FooTest : public testing::TestWithParam<s> {};
    
    TEST_P(FooTest, DoesBlah) {}
    
    INSTANTIATE_TEST_SUITE_P(MeenyMinyMoe, FooTest, testing::Values(s()));
    
    $ g++ -O3 -Wall -g -o test test.cpp -lgtest -lgtest_main
    $ valgrind --track-origins=yes ./test 
    ==73422== Memcheck, a memory error detector
    ==73422== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==73422== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
    ==73422== Command: ./test
    ==73422== 
    Running main() from ./googletest/src/gtest_main.cc
    custom new for size 1
    [==========] Running 1 test from 1 test suite.
    [----------] Global test environment set-up.
    [----------] 1 test from MeenyMinyMoe/FooTest
    [ RUN      ] MeenyMinyMoe/FooTest.DoesBlah/0
    [       OK ] MeenyMinyMoe/FooTest.DoesBlah/0 (6 ms)
    [----------] 1 test from MeenyMinyMoe/FooTest (10 ms total)
    
    [----------] Global test environment tear-down
    [==========] 1 test from 1 test suite ran. (38 ms total)
    [  PASSED  ] 1 test.
    ==73422== 
    ==73422== HEAP SUMMARY:
    ==73422==     in use at exit: 0 bytes in 0 blocks
    ==73422==   total heap usage: 217 allocs, 217 frees, 114,909 bytes allocated
    ==73422== 
    ==73422== All heap blocks were freed -- no leaks are possible
    ==73422== 
    ==73422== For lists of detected and suppressed errors, rerun with: -s
    ==73422== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
    

    Also check this answer for a better explanation.

    Unfortunately I could not come up with a global replacement for new. If I use the example code in that page I get a ton of mismatched-new-delete warnings and running the resulting binary with valgrind generates a ton of Mismatched free() / delete / delete [] errors.

    Cheers!