View Issue Details

IDProjectCategoryView StatusLast Update
00008241003.1(2013)/Issue7+TC1System Interfacespublic2019-06-10 08:54
Reportereblake Assigned To 
PrioritynormalSeverityObjectionTypeError
Status ClosedResolutionAccepted 
NameEric Blake
OrganizationRed Hat
User Referenceebb.posix_spawn
Sectionposix_spawn
Page Number1436
Line Number47378, 47385
Interp StatusApproved
Final Accepted TextSee 0000824:0002164
Summary0000824: children should not inherit fcntl file locks from parent calling posix_spawn
DescriptionThe posix_spawn interface requires (line 47448):

All process attributes, other than those influenced by the attributes set in the object referenced by attrp as specified above or by the file descriptor manipulations specified in file_actions, shall appear in the new process image as though fork( ) had been called to create a child process and then a member of the exec family of functions had been called by the child process to execute the new process image.

It also requires that whether file_actions is NULL (line 47378) or not (line 47385), that for file descriptors that remain open in the child "all attributes of the corresponding open file descriptions, including file locks (see fcntl( )), shall remain unchanged."

However, this contradicts wording elsewhere in the standard. The description of fcntl states (line 27302): "An exclusive lock shall prevent any other process from setting a shared lock or an exclusive lock on any portion of the protected area." and (line 27337) "Locks are not inherited by a child process."

Likewise, fork states (line 29714) "File locks set by the parent process shall not be inherited by the child process."

Note that locks CAN be inherited across exec (line 26070): "Locks that are not removed by closing of file descriptors remain unchanged." - but in this case, there is only one process owning the lock, since exec is not creating a new process, but reusing an existing one.

Implementation wise, it is impossible to have both parent and child own an exclusive lock across posix_spawn, since only one process can own an exclusive lock at a time; and it does not make sense to inherit shared locks if it is impossible to inherit exclusive locks. Furthermore, the intent of posix_spawn is to create a child that is independent enough from the parent that an implementation might be able to implement it in a manner other than using fork, but that a conforming implementation can be built by using fork (see XRAT B.3.3 for a sample implementation - and overlook the bugs in that sample such as not being robust to a posix_spawn_file_actions_addopen of a file name containing '*'). Since an implementation built on fork drops locks, it makes the most sense to assume the requirement on posix_spawn to preserve locks into the child is a mistake.

To demonstrate existing practice, as further evidence that the standard is wrong, this sample program shows that the parent continues to own the lock (as the parent does not detect any problems in a request to change from shared to exclusive), while the child detects a conflict in trying to request a write lock because the parent still owns a lock. If the child had indeed inherited a lock, then the child would have the same behavior as the parent when calling detect():

#include <stdio.h>
#include <spawn.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
extern char **environ;

static struct flock l;

int detect(void)
{
    l.l_type = F_WRLCK;
    if (fcntl(3, F_GETFD) < 0) {
        perror("fd 3 not open");
        return 1;
    }
    if (fcntl(3, F_GETLK, &l) < 0)
        perror("getlock failed");
    else if (l.l_type == F_UNLCK)
        printf("%d: no lock detected\n", (int) getpid());
    else
        printf("%d: conflicting lock type %d, whence %d, start %lld, "
               "len %lld, owner %d\n",
               (int) getpid(), l.l_type, l.l_whence, (long long) l.l_start,
               (long long) l.l_len, (int) l.l_pid);
    return 0;
}

int main(int argc, char **argv)
{
    l.l_type = F_RDLCK;
    l.l_whence = SEEK_SET;
    l.l_start = 0;
    l.l_len = 0;
    if (argc == 1) {
        printf("In child %d\n", (int) getpid());
        return detect();
    }
    pid_t p;
    char *newargv[] = { argv[0], NULL };
    int fd = open("file", O_RDWR | O_CREAT, 0600);
    if (fd < 0) {
        perror("cannot create file");
        return 1;
    } else if (fd != 3) {
        printf("unexpected fd %d\n", fd);
        return 1;
    }
    if (fcntl(3, F_SETLK, &l) < 0) {
        perror("could not lock");
        return 1;
    }
    detect();
    printf("In parent %d, before spawn, fd = %d\n", (int) getpid(), fd);
    posix_spawn(&p, argv[0], NULL, NULL, newargv, environ);
    sleep(1);
    printf("In parent, after spawn\n");
    detect();
    close(fd);
    unlink("file");
    return 0;
}

$ ./a 1
14034: no lock detected
In parent 14034, before spawn, fd = 3
In child 14035
14035: conflicting lock type 0, whence 0, start 0, len 0, owner 14034
In parent, after spawn
14034: no lock detected
Desired ActionAt line 47378 page 1436 (posix_spawn DESCRIPTION), change:
For those file descriptors that remain open, all attributes of the corresponding open file descriptions, including file locks (see fcntl( )), shall remain unchanged.
to:
For those file descriptors that remain open, the child process shall not inherit any file locks, but all remaining attributes of the open file descriptions (see fcntl( )) shall remain unchanged.


At line 47385, change:
All attributes of the corresponding open file descriptions, including file locks (see fcntl( )), shall remain unchanged.
to:
The child process shall not inherit any file locks, but all remaining attributes of the corresponding open file descriptions (see fcntl( )) shall remain unchanged.
Tagstc2-2008

Relationships

related to 0000768 Closed add "fd-private" POSIX locks to spec 

Activities

Don Cragun

2014-02-27 16:21

manager   bugnote:0002164

Last edited: 2014-02-27 16:24

Interpretation response
------------------------
The standard states that locks are inherited when using posix_spawn() and are not inherited when using fork()/exec(), and conforming implementations must conform to this. However, concerns have been raised about this which are being referred to the sponsor.

Rationale:
-------------
The behavior seen when creating a child using posix_spawn() and when using fork()/exec() should be the same.

Notes to the Editor (not part of this interpretation):
-------------------------------------------------------
Make the changes suggested in the Desired Action.

ajosey

2014-02-28 10:19

manager   bugnote:0002169

Interpretation Proposed 28 Feb 2014

ajosey

2014-04-01 13:32

manager   bugnote:0002211

Interpretation approved 1 April 2014

Issue History

Date Modified Username Field Change
2014-02-12 00:29 eblake New Issue
2014-02-12 00:29 eblake Name => Eric Blake
2014-02-12 00:29 eblake Organization => Red Hat
2014-02-12 00:29 eblake User Reference => ebb.posix_spawn
2014-02-12 00:29 eblake Section => posix_spawn
2014-02-12 00:29 eblake Page Number => 1436
2014-02-12 00:29 eblake Line Number => 47378, 47385
2014-02-12 00:29 eblake Interp Status => ---
2014-02-12 00:32 eblake Desired Action Updated
2014-02-27 16:21 Don Cragun Note Added: 0002164
2014-02-27 16:21 Don Cragun Status New => Interpretation Required
2014-02-27 16:21 Don Cragun Resolution Open => Accepted
2014-02-27 16:22 eblake Relationship added related to 0000768
2014-02-27 16:22 Don Cragun Interp Status --- => Pending
2014-02-27 16:22 Don Cragun Final Accepted Text => See 0000824:0002164
2014-02-27 16:22 Don Cragun Tag Attached: tc2-2008
2014-02-27 16:24 Don Cragun Note Edited: 0002164
2014-02-28 10:19 ajosey Interp Status Pending => Proposed
2014-02-28 10:19 ajosey Note Added: 0002169
2014-04-01 13:32 ajosey Interp Status Proposed => Approved
2014-04-01 13:32 ajosey Note Added: 0002211
2019-06-10 08:54 agadmin Status Interpretation Required => Closed