Are the shm_open and ftruncate conditions possible?

On the shm_open man page:

The new shared memory object initially has zero length. The size of the object can be set using ftruncate (2). [...] The shm_open () function itself does not create a common object of a certain size, because this will lead to duplication of the existing function, which sets the size of the object that the file descriptor refers to.

Does this app condition race conditions? Consider the following pseudo code:

 int fd = shm_open("/foo", CREATE); if ( fd is valid ) { // created shm object, so set its size ftruncate(fd, 128); } else { fd = shm_open("/foo", GET_EXISTING); } void* mem = mmap(fd, 128); 

Since the calls to shm_open and ftruncate are not atomic, you might have a race condition in which one process calls shm_open ( CREATE case), but before calling ftruncate another process calls shm_open ( GET_EXISTING case) and tries an mmap object of size 0 and maybe even write to it.

I can think of two ways to avoid this race condition:

  • Using the IPC mutex / semaphore to synchronize an entire object or ...

  • If it is safe (for POSIX), call ftruncate for CREATE and GET_EXISTING .

What is the preferred method to prevent this race condition?

+4
source share
2 answers

Your approach (calling ftruncate from both) should work, but you need a way to synchronize the use of the contents of the shared memory segment anyway. Since the memory is initially empty (filled with zero) and therefore does not contain a valid synchronization object, if you are not going to use your own atomism, you will need a secondary synchronization form to control access to shared memory.

I would think normally, instead of having several technological races to create or open a shared memory segment with a fixed name, you would like the owner process to be responsible for creating a segment with a random name using O_EXCL to avoid accidental or malicious collisions, and then passing the name as soon as you successfully opened it, set the size and create synchronization objects in it, to other processes that should be able to access it.

+4
source

Like @R. that another problem is that, having created the file, there is still a window in front of the contents, such as a mutex, initialized and ready for use.

A slightly different solution above:

Try to open (). If the open () function succeeds, simply match () and use it with the necessary guarantee (see below) that the content is already initialized and it is useful to continue. If open () fails, create and initialize the temporary file, then try the hard link () temporary file as the desired file and cancel () the temporary name.

If link () succeeds, we now make the initialized file available to ourselves and to other processes. If link () fails with EEXIST, another process got there first (unlike rename (), link () fails if target name exists). In any case, our repeated open () should now succeed with the initialized ready-to-use file.

With this strategy, there is clearly a race condition for initializing a temporary file, however, provided that the initialization process is idempotent and not too expensive for resources, and each process selects a unique temporary file, it does not matter. If multiple initialization can be a problem, the solution is to split the initialization into a two-step process, with the first step being just a mutex in the file to use to protect the rest of the file from being multiple-initialized in the second step.

0
source

Source: https://habr.com/ru/post/1480295/


All Articles