Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

shm_open() fails with EINVAL when creating shared memory in subdirectory of /dev/shm

I have a GNU/Linux application with uses a number of shared memory objects. It could, potentially, be run a number of times on the same system. To keep things tidy, I first create a directory in /dev/shm for each of the set of shared memory objects.

The problem is that on newer GNU/Linux distributions, I no longer seem to be able create these in a sub-directory of /dev/shm.

The following is a minimal C program with illustrates what I'm talking about:

/*****************************************************************************
* shm_minimal.c
*
* Test shm_open()
*
* Expect to create shared memory file in:
*  /dev/shm/
*  └── my_dir
*      └── shm_name
*
* NOTE: Only visible on filesystem during execution.  I try to be nice, and
*       clean up after myself.
*
* Compile with:
*   $ gcc -lrt shm_minimal.c -o shm_minimal
*
******************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>


int main(int argc, const char* argv[]) {
  int shm_fd = -1;

  char* shm_dir = "/dev/shm/my_dir";
  char* shm_file = "/my_dir/shm_name";      /* does NOT work */
  //char* shm_file = "/my_dir_shm_name";    /* works */

  // Create directory in /dev/shm
  mkdir(shm_dir, 0777);

  // make shared memory segment
  shm_fd = shm_open(shm_file, O_RDWR | O_CREAT, 0600);

  if (-1 == shm_fd) {

    switch (errno) {
    case EINVAL:
      /* Confirmed on:
       *  kernel v3.14, GNU libc v2.19  (ArchLinux)
       *  kernel v3.13, GNU libc v2.19  (Ubuntu 14.04 Beta 2)
       */
      perror("FAIL - EINVAL");
      return 1;

    default:
      printf("Some other problem not being tested\n");
      return 2;
    }

  } else {
    /* Confirmed on:
     *  kernel v3.8, GNU libc v2.17    (Mint 15)
     *  kernel v3.2, GNU libc v2.15    (Xubuntu 12.04 LTS)
     *  kernel v3.1, GNU libc v2.13    (Debian 6.0)
     *  kernel v2.6.32, GNU libc v2.12 (RHEL 6.4)
     */
    printf("Success !!!\n");
  }

  // clean up
  close(shm_fd);
  shm_unlink(shm_file);
  rmdir(shm_dir);
  return 0;
}


/* vi: set ts=2 sw=2 ai expandtab:
 */

When I run this program on a fairly new distribution, the call to shm_open() returns -1, and errno is set to EINVAL. However, when I run on something a little older, it creates the shared memory object in /dev/shm/my_dir as expected.

For the larger application, the solution is simple. I can use a common prefix instead of a directory.

If you could help enlighten me to this apparent change in behavior it would be very helpful. I suspect someone else out there might be trying to do something similar.

like image 540
Pablo Maurin Avatar asked Dec 05 '22 06:12

Pablo Maurin


1 Answers

So it turns out the issue stems from how GNU libc validates the shared memory name. Specifically, the shared memory object MUST now be at the root of the shmfs mount point.

This was changed in glibc git commit b20de2c3d9 as the result of bug BZ #16274.

Specifically, the change is the line:

if (name[0] == '\0' || namelen > NAME_MAX || strchr (name, '/') != NULL)

Which now disallows '/' from anywhere in the filename (not counting leading '/')

like image 52
Pablo Maurin Avatar answered Dec 28 '22 08:12

Pablo Maurin