tags:

views:

395

answers:

4
Collection CollectionFactory::createFromMap(const std::string& name,
        const DataMap& dm) const
{
    if (!Collection::isNameValid(name))
    {
        const std::string error = "invalid collection name";
        throw std::invalid_argument(error);
    }
    Collection c(name, dm);
    dm.initDataCollection(&c, true);
    return c;
}

Whenever the throw statement is executed, I'm getting a segmentation fault. Here is the cause from Valgrind output. I've no idea what's going on.

==21124== Invalid read of size 1
==21124==    at 0x41D2190: parse_lsda_header(_Unwind_Context*, unsigned char const*, lsda_header_info*) (eh_personality.cc:62)
==21124==    by 0x41D24A9: __gxx_personality_v0 (eh_personality.cc:228)
==21124==    by 0x4200220: _Unwind_RaiseException (unwind.inc:109)
==21124==    by 0x41D2C9C: __cxa_throw (eh_throw.cc:75)
==21124==    by 0x4079BFB: corestore::CollectionFactory::createFromMap(std::string const&, corestore::DataMap const&) const (CollectionFactory.C:43)
==21124==    by 0x8188F86: CollectionFactoryTest::testCreateNewFromMap_InvalidName() (CollectionFactoryTest.C:91)
==21124==    by 0x81895D3: CppUnit::TestCaller<CollectionFactoryTest>::runTest() (TestCaller.h:166)
==21124==    by 0x40D1BB5: CppUnit::TestCaseMethodFunctor::operator()() const (TestCase.cpp:34)
==21124==    by 0x40C18E3: CppUnit::DefaultProtector::protect(CppUnit::Functor const&, CppUnit::ProtectorContext const&) (DefaultProtector.cpp:15)
==21124==    by 0x40CD0FC: CppUnit::ProtectorChain::ProtectFunctor::operator()() const (ProtectorChain.cpp:20)
==21124==    by 0x40CCA65: CppUnit::ProtectorChain::protect(CppUnit::Functor const&, CppUnit::ProtectorContext const&) (ProtectorChain.cpp:77)
==21124==    by 0x40DC6C4: CppUnit::TestResult::protect(CppUnit::Functor const&, CppUnit::Test*, std::string const&) (TestResult.cpp:178)
==21124==  Address 0xc82f is not stack'd, malloc'd or (recently) free'd

I've had several iterations of the unit test that's bombing, but here is the current one that exhibits the same bug as all the others:

void CollectionFactoryTest::testCreateNewFromMap_InvalidName()
{
    const char* MAP_FILE =
            "smallMapWithThreeSets.xml";
    const char* NAME1 = "name/invalidname";
    const char* NAME2 = "name/invalidname";

    DataMapReader dmr;
    DataMap dm = dmr.getDataMapFromFile(MAP_FILE);

    CollectionFactory cf;
    try
    {
        cf.createFromMap(NAME1, dm);
    }
    catch (std::exception const& e)
    {
        std::cerr << e.what() << std::endl;
    }

    /*CPPUNIT_ASSERT_THROW(cf.createFromMap(NAME1, dm), std::invalid_argument);
    CPPUNIT_ASSERT_THROW(cf.createFromMap(NAME2, dm), std::invalid_argument);*/
}

Per request, the contents of isNameValid:

bool Collection::isNameValid(const std::string& name)
{
    /* can't be blank */
    if(name.length() == 0)
    {
        return false;
    }
    /* Can't contain '/' */
    if(name.find('/') != std::string::npos)
    {
        return false;
    }
    return true;
}
A: 

You need to handle the exception somewhere in your code (e.g the caller of createFromMap or somewhere higher in the call-chain) or else the system calls std::unexpected which in turn calls the default terminate handler -- std::terminate and you end up with a crash.

Try wrapping the call by a try-catch block:

 try {
    ...
    createFromMap(...);
    ...
 } catch (std::exception const& e) {
    std::cerr << e.what() << std::endl;
 }
dirkgently
The unit test is using CPPUNIT_ASSERT_THROW which is expanded as in this link: http://cppunit.sourceforge.net/doc/lastest/group___assertions.html#ga6
Dave
std::terminate will **cleanly** terminate the process - in and of itself it shouldn't cause a segfault nor should it trigger a Valgrind warning.
R Samuel Klatchko
You probably have some other exceptions which are not being handled at all. Unwind the macro, add a catch-all with a print statement, recompile, and step through the debugger to see where you hit the first exception (you'll go straight to one of the catch blocks).
dirkgently
@R Samuel Klatchko: Can you give me a pointer where the it states that termination will be clean?
dirkgently
I tried both catch(...) and the example code in the above answer instead of using the CPPUNIT macro and am getting the same crash.
Dave
@Dave: Which would imply, the crash is caused due to something else. Can you at least post `CollectionFactoryTest::testCreateNewFromMap_InvalidName()` body?
dirkgently
Make sure that `Collection::isNameValid(name)` is not throwing any exceptions.
dirkgently
@dirkgently - only indirectly. I see nothing that states that terminate is allowed to invoke undefined behavior.
R Samuel Klatchko
@dirkgently isNameValid cannot throw any exceptions. It's just doing string::length() and string::find(char) and just simple if checks to return false or true.
Dave
@Dave: Is the string an empty one? Post the body of isNameValid.
dirkgently
@dirkgently I added it to the question.
Dave
I compiled my main library and test with g++ instead of Intel's C++ compiler (icpc) and it worked. Any ideas why this would be the case? I have to use Intel's compiler.
Dave
@Dave: I am tempted to ascribe this behavior to a bug in the icpc :-) Can you check with the documentation of icpc to see if your system is supported? Also, try searching their bug bases (provide all the details -- such as command line options you are using). E.g: They seem to have some issues with openmp and O2/O3 flags.
dirkgently
+1  A: 

Is that the very first Valgrind error or are there previous ones?

My guess is that there are previous ones and one of those is corrupting memory and causing throw to break.

R Samuel Klatchko
All of the previous errors involve either uninitialized values in system calls (sigaction, write) or conditional jump on uninitialized values, so these aren't corrupting memory.
Dave
Can you reduce the problem? If you only run testCreateNewFromMap_InvalidName do you still get the segfault?
R Samuel Klatchko
I created a separate program. It's just main. All it does is what was in that test. Same segmentation error occurs. I also created a separate program that just throws an exception (doesn't use my library), and it works. Also, if I don't catch, I get a clean exit with a print out of "Aborted".
Dave
Can you simplify some more. What if you get rid of DataMapReader and just use an empty DataMap (since the exception is thrown before you use the DataMap, it shouldn't matter).
R Samuel Klatchko
Yup. Did that and the problem remains.
Dave
A: 

How are you linking the code? If you're creating a shared library for instance, it's possible that you need to specify compilation flags for position-independent code, such as -fPIC (gcc/g++).

Tom
The exceptions are being thrown within a shared library. Indeed, when I link statically, the bug goes away. However, at work, we have two OS configurations. The newer 64-bit system uses the 11.1 Intel Compiler instead of the old 9.1. It also uses gcc4+ instead of 3.3, and it is working on the newer system. I'm waiting to work with intel to see if this is a known issue with the 9.1 compiler.
Dave
+1  A: 

Dave, there is missing code in your execution path, which prevents from nailing down the problem:

  • DataMapReader constructor;
  • implementation of DataMapReader::getDataMapFromFile();
  • DataMap constructor;
  • CollectionFactory constructor;

I'd recommend trying stripping down the testcase as much as possible while the segmentation fault is still reproducible.

I have the following guesses for the problem:

  • DataMapReader::getDataMapFromFile() accepts a reference to std::string and then stores it either in static memory or in DataMapReader instance or in DataMap instance. This leads to undefined behavior, because the referenced std::string object will be destroyed immediately after exiting the DataMapReader::getDataMapFromFile(), so all remaining references automatically become invalid.
  • DataMapReader::getDataMapFromFile() returns a reference to DataMap instance stored on a stack. Such an instance will be destroyed as soon as the DataMapReader::getDataMapFromFile() returns and before copy constructor for dm will be called, which leads to undefined behavior. This case is unlikely, because compilers usually warn about returning pointers or references to objects stored on a stack.
  • If DataMapReader::getMapFromFile() returns DataMap by value and DataMap has no explicitly defined copy constructor and/or copy assignment operator, or it has incorrect implementations for those constructor and/or operator, which improperly handles copying of pointers and/or references to members owned by the DataMap (or other members stored by value have the same problem recursively). In this case referenced members can be destroyed in DataMap destructor called for the instance stored on a stack before returning from the DataMapReader::getMapFromFile(), which leads to undefined behavior.

Though this isn't related to the segmentation fault mentioned in the question, the same potential problems can apply to the code below throwing exception in the CollectionFactory::createFromMap():

  • Collection constructor shouldn't store references to input parameters (std::string and DataMap) if theirs lifetime is less than the lifetime of Collection instance.
  • DataMap.initDataCollection() shouldn't store pointer to the Collection instance, because the lifetime of the Collection instance created on a stack is less than the lifetime of the DataMap instance. Collection instance will be destroyed before returning from the CollectionFactory::createFromMap().
  • Collection should have properly implemented copy constructor and/or copy assignment operator, otherwise it will suffer from the problem outlined above.
valyala