Search code examples
clinuxposixshared-memory

Why, on Linux (specifically Ubuntu 20.04 LTS), a POSIX shared memory object survives reboot and then suddenly belongs to the "root" user?


Important notice

The problem is not resolved at all and it was not caused by a typo. Just because some person said that they cannot reproduce it (and they may be right, as far as their local test setup is concerned), it does not mean the problem doesn't exist for me. I can perfectly reproduce the problem and I'm still looking for a solution or explanation.

If any more information is needed, please tell me what is needed.

Demonstration

Here is a short video clip that demonstrates the problem.


Original Question

I'm using a POSIX shared memory to share some common state between multiple instances of my application. The POSIX shared memory is created via the shm_open() function, it is expanded to the required size via ftruncate() and it is then mapped into the memory via mmap() function. I also create a Semaphore, via the sem_open() function, which is used to synchronize the access to the shared memory and to make sure that the first instance performs the initialization.

So far, this seems to be working fine. And I also see that my shared memory objects appear under /dev/shm/ with the proper owner and permissions, all as expected. But, on Linux (Ubuntu 20.04 LTS) I'm seeing some really strange behavior: After a reboot, the shared memory object (and the semaphore too!) is still there. How is that even possible? After all, to the best of my knowledge, shared memory objects are volatile, i.e. the /dev/shm/ filesystem is a tmpfs that lives entirely in RAM and therefore can not survive a reboot. Even worse, after the reboot, the owner of my shared memory object has changed from the original user to "root" for no apparent reason! This means that if I launch my application again, after a reboot, then the shm_open() fails with permission denied! So, there must be "something" that persists the shared memory to disk when the system is going down, and that restores them when the system is booting up again. But why the mysterious owner change that makes the "persisted" shared memory inaccessible?

Note: I have checked this on MacOS X and FreeBSD, but neither seems to have this behavior.

I tested, multiple times, that, on Linux, ls -al /dev/shm shows the original owner for my shared memory (and semaphore) just before the reboot, and that it shows "root" as the owner just after the reboot. The application did not run in between the two ls -al /dev/shm invocations. Only way to get things working again is to sudo rm -f /dev/shm/* after each reboot. But that cannot be the "solution", right? What is the "standard" way to deal with this issue on Linux ???


My code looks like this:

static unsigned char *initialize(const int create_shmem)
{
    void *buffer = NULL;

    int fd = shm_open(OBJECT_NAME, create_shmem ? O_CREAT | O_EXCL | O_RDWR : O_RDONLY, 0644);
    if (fd < 0) {
        if ((!create_shmem) && (errno == ENOENT)) {
            return initialize(1);
        } else {
            return NULL;
        }
    }

    if (create_shmem) {
        if (ftruncate(fd, BUFFER_SIZE) != 0) {
            goto failure;
        }
    }

    buffer = mmap(NULL, BUFFER_SIZE, create_shmem ? PROT_READ | PROT_WRITE : PROT_READ, MAP_SHARED, fd, 0U);
    if (buffer == MAP_FAILED) {
        goto failure;
    }

    /* ... */
}

I know that I could explicitly remove the shared memory, via shm_unlink() function, prior to the reboot, which would probably avoid the problem. But this is really not reliable! If once the application misses a proper clean-up for whatever reason (e.g. crash) and then the system is rebooted, we end up with a shared memory object that can no longer be accessed.


Interestingly, I found that systemd login manager has a configuration option RemoveIPC which "Controls whether System V and POSIX IPC objects belonging to the user shall be removed when the user fully logs out". However, this does not seem to effect shared memory objects belonging to the "root" user ("Note that IPC objects of the root user and other system users are excluded from the effect of this setting"). Also this does not explain why, on reboot, the ownership of existing shared memory objects is transferred from the previous owner to the "root" user, instead of just deleting the shared memory. Deletion would be okay, because then we would be able to just re-create it, as the normal user. There seems to be no similar option to have "root"-owned objects deleted too.

Source


Does Linux offer a sort of "boot counter", which is incremented every time that the system is powered on or rebooted? If so, then I could just append that counter to the name of my object in order to work around the problem of "old" objects (names) being blocked by the "root" user.


I have now created a systemd service unit that runs when the system is shutting down and that calls a script which deletes everything from /dev/shm directory. From my log I can see (later) that my "delete" script actually ran when the system was shutting down. And I can see that it actually deleted the existing shared memory from the /dev/shm directory. Nonetheless, when I inspect /dev/shm after the reboot, the shared memory is back and is now owned by "root" user!!

I can only assume that this is happening because "whatever" is saving the contents of /dev/shm to disk when the system is rebooting does this before my delete script gets a chance to run...


Solution

  • As noted by a comment, on Linux, we can use /proc/sys/kernel/random/boot_id to get a unique ID that changes on every reboot, but that remains fixed as long as the system is running. This is a great way to generate a unique name for my POSIX shared memory, such that different processes can agree on the same name, but still that name changes after a reboot. Hence we can avoid name conflicts with "old" shared memories that are no longer accessible.