Search code examples
c++11inheritanceipcshared-memory

Why does inheritance cause shared memory segmentation fault?


I'm trying to use a class instance in shared memory, shared between two processes. What I create and use a base class there are no issues. However, when that class inherits from another class, the shared memory instance causes segfaults.

Why does using a class that inherits cause a segfault?

I'm running building/running/debugging the code on a Redhat Linux VM (RHEL 7.6), using GCC 4.8.5.

So far, I set up the shared memory and tested my usage of it with a char, a simple class that holds a char with a setter/getter, and with a pure abstract class, which the simple char class inherits. The shared memory works fine in both former cases, but segfaults in the third case.

In the crash case, the first process creates a key with ftok, gets a block of shared memory equal in size to the class I'm using, then attaches to that memory and uses the placement new operator to create a class instance in shared memory. Then, the second process creates the same key, gets the block of memory, attaches to that block of memory, and tries to access it.

At this point, if the second process attempts to use the memory (call methods of the class) it will segfault. However, it can create a new object at the place in shared memory, but that will cause the first process to segfault on access.

Here is the class setup:

class MyClassInterface {
 public:
};

class MyClass : public MyClassInterface {
 private:
  char val;
 public:
  MyClass() {
    val = 'a';
  }
  ~MyClass() {
    std::cout << "MyClass destructor called" << std::endl;
  };

  void SetVal(char c) {
    val = c;
  }

  char GetVal() {
    return val;
  }
};

Here is what shared memory control looks like in process 1:

MyClass *test_val;

int main(int argc, char **argv) {
  ...

  key_t key = ftok("/tmp", 127);
  if (key < 0) {
    exit(EXIT_FAILURE);
  }

  int shmid = shmget(key, sizeof(MyClass), 0666 | IPC_CREAT);
  MyClass *shmem = (MyClass *) shmat(shmid, nullptr, 0);
  if (shmem == (void *) -1) {
    exit(EXIT_FAILURE);
  }

  test_val = new(shmem) MyClass;
  test_val->SetVal('b');


  std::cout << "Val: " << test_val->GetVal() << std::endl;

And here is the shared memory control in process 2:

int main(int argc, char **argv) {
  ...

  key_t key = ftok("/tmp", 127);
  if (key < 0) {
    exit(EXIT_FAILURE);
  }

  int shmid = shmget(key, sizeof(MyClass), 0666 | IPC_CREAT);
  MyClass *shmem = (MyClass *) shmat(shmid, nullptr, 0);
  if (shmem == (void *) -1) {
    exit(EXIT_FAILURE);
  }

  MyClass *ipc_dc = new(shmem) MyClass;
  ipc_dc->SetVal('z');

  std::cout << "Val: " << ipc_dc->GetVal() << std::endl;

What I want is to be able to use a class that inherits from a pure abstract class in shared memory. The actual use will include usage of semaphores to protect the members of the class.

My expectation is that after initialization the two processes can use the class object and update its value freely (protected by semaphores, of course). However, attempting to access the class in any way (when using inheritance) just causes a segmentation fault. It seems like there is something preventing both processes from having access to that memory at the same time.

Am I doing something wrong with the inheritance? Am I doing something wrong with how I'm using shared memory with a class? Both?


Solution

  • As some-programmer-dude pointed out, the issue is that pure abstract classes use virtual functions, which don't map to the same location in each process accessing the object. Attempting to use a function mapped into the vtable at a wrong address causes the segfault, which makes sense.

    In my case, I need the base class for use with GMock, but not for the actual application. Added pre-processor directives around the class definition allows the "interface" to be implemented and used when building for GTest/GMock (where shared memory isn't actually used), but left out when building for the target executable.

    So then, the answer here is to not use inheritance with objects that will occupy shared memory.