user7005976
user7005976

Reputation: 415

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)

Upvotes: 3

Views: 1901

Answers (2)

Leonardo
Leonardo

Reputation: 1901

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!

Upvotes: 3

Paul Floyd
Paul Floyd

Reputation: 6946

A quick google for the gtest source:

https://clickhouse.com/codebrowser/html_report/ClickHouse/contrib/googletest/googletest/src/gtest-printers.cc.html

GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_
GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_
GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_
void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start,
                                size_t count, ostream* os) {
  char text[5] = "";
  for (size_t i = 0; i != count; i++) {
    const size_t j = start + i;
    if (i != 0) {
      // Organizes the bytes into groups of 2 for easy parsing by
      // human.
      if ((j % 2) == 0)
        *os << ' ';
      else
        *os << '-';
    }
    GTEST_SNPRINTF_(text, sizeof(text), "%02X", obj_bytes[j]);
    *os << text;
  }
}

With gdb you may be able to get from there back to your code. Only 15 layers to wade through.

Upvotes: 0

Related Questions