Summary:
I have a class implementing shared memory using Boost Interprocess. A segmentation fault occurs from a method read()
which accesses the named_condition
. When I look at the values of nearby std::string
class members in GDB they are corrupted too. One contains the value of a string literal I only log via a third party macro.
I am going to describe the problem, show you the code which sets the intended string values and then ask whether I am causing the problem, or is it the third party logger?
I am using Clang (not GCC).
This problem is incredibly difficult to reproduce. It occurs once every 300+ runs.
Details:
This is my object containing the Boost Interprocess named_condition and nearby std::string
s which I noticed had corrupted values:
char a[1000];
std::shared_ptr<bip::named_mutex> mutex{nullptr};
char b[1000];
MyVector* vec{nullptr};
std::shared_ptr<bip::managed_shared_memory> segment{nullptr};
std::shared_ptr<bip::named_condition> cond_empty; // Seg fault
bool destroy_memory{false};
std::string shared_vector_name; // Weird value
std::string shared_mutex_name; // Weird value
std::string shared_cv_name; // Weird value
std::string shared_memory_name; // Weird value
std::string tag_name;
The two char arrays were added weeks ago to detect (probably this same) segmentation fault.
The segmentation fault occurs when I try to access the named_condition when attempting to read the shared memory:
std::vector<T> read(const bool clearAfterReading, const bool readImmediately = false)
{
checkMemory(); // Loops over 'a' and checks for corruption
std::vector<T> readItems;
std::cout << "Reader trying to obtain mutex" << std::endl;
bip::scoped_lock<bip::named_mutex> lock(*sdc.mutex);
if(sdc.vec->empty() && readImmediately == false)
{
std::cout << "Reader waiting. vec size: " << sdc.vec->size() << std::endl;
sdc.cond_empty->wait(lock); //SEG FAULT OCCURS HERE
When I look at the state of sdc.cond_empty in GDB I notice m_base = 0x0
:
(gdb) p *(sdc.cond_empty._M_ptr)
$12 = {m_cond = {m_shmem = {<boost::interprocess::ipcdetail::managed_open_or_create_impl_device_holder<false, boost::interprocess::shared_memory_object>> = {<No data fields>}, static ManagedOpenOrCreateUserOffset = 16,
m_mapped_region = {m_base = 0x0, m_size = 104, m_page_offset = 0, m_mode = boost::interprocess::read_write, m_is_xsi = false}}}}
so I assume I am correct that this named_condition has been corrupted?
I then decided to check the values of surrounding std::string class members (scroll right to end):
(gdb) p sdc.shared_vector_name
$22 = {static npos = <optimized out>, _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x7fff80a0d438 "HandleRunRequest()"}}
(gdb) p sdc.shared_mutex_name
$23 = {static npos = <optimized out>, _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x7fff80a0d468 "File exists"}}
(gdb) p sdc.shared_cv_name
$24 = {static npos = <optimized out>, _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x7fff80a0d498 "/abc_shared_memory"}}
(gdb) p sdc.shared_memory_name
$25 = {static npos = <optimized out>, _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x7fff80a0d408 ""}}
(gdb) p sdc.tag_name
$26 = {static npos = <optimized out>, _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x7fffc021d598 "abc"}}
and I notice their values are completely wrong. The values appear to be coming from string literals I log via a third party macro.
The intended std::string values are set via the following code:
This const:
extern "C"
{
const std::string ABC_SH_MEM_NAME = "abc";
}
is passed through the shared memory reader constructor:
sharedMemReader(CONSTS::ABC_SH_MEM_NAME, CONSTS::ABC_SH_MEM_SIZE, true),
via the tag argument:
SharedDataReader(const std::string& tag, const int numBytes, const bool destroyMemory)
{
sdc.initialise(tag, numBytes, destroyMemory);
so hard-coded strings can be appended to tag
and assigned to the aforementioned string class members:
void initialise(const std::string& tag, const int numBytes, const bool ownMemory)
{
std::cout << std::endl << "Started initialisation..." << std::endl;
const std::string sharedMemoryName = tag + "_shared_memory";
const std::string sharedVectorName = tag + "_shared_vector";
const std::string sharedMutexName = tag + "_shared_mutex";
const std::string sharedCVName = tag + "_shared_cv";
tag_name = tag;
shared_memory_name = sharedMemoryName;
shared_mutex_name = sharedMutexName;
shared_vector_name = sharedVectorName;
shared_cv_name = sharedCVName;
destroy_memory = ownMemory;
As mentioned previously, shared_vector_name
is corrupted with the value "HandleRunRequest()".
My only ever usage of this string/value is calling a third party logging macro:
void MyClass::HandleRunRequest()
{
THIRD_PARTY_LOG_MACRO("HandleRunRequest()");
but this method is not called immediately-near read()
, where the seg fault occurs. This method calls a few other methods, before spawning a new thread, which then calls read()
.
Is there absolutely anything I could be doing to cause this corruption? Or, is the third party logging storing values in random pointer addresses?
I still have the core dump and library, but please be aware this problem is incredibly difficult to reproduce.