throwing an exception causes segmentation fault
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_persona开发者_开发知识库lity.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;
}
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.
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.
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++).
精彩评论