2

I read Stevens' example on passing descriptors between processes. To summarize, his main program forks a child which exec another program, which opens a file, passes the integer fd back to parent via unix domain socket and exit. Parent receives this fd from the socket, and directly reads the file using the fd.

Two questions come up:

  1. Parent and child are two separate processes, therefore unless they share file descriptor table(which is NOT the default fork behavior, since CLONE_FILES is not set, AFAIK), parent wouldn't be able to use the fd from child directly. Parent needs to find a slot in its descriptor array and map it to the file object made by child. Stevens mentioned the issue, but the code does not seem to do this mapping on receiving side.
  2. The file object made by child will be freed upon process exit, if child does not increase refcount. Again, Stevens mentioned this in description leading up to the code but the code itself does not seem to do this.

I found a related SO post, where the role of parent and child is reversed, otherwise it is the same as Stevens' example. Not sure how that one works either.

Am I missing something here? My guesses are based on Linux, maybe unix is different enough that those two issues are somehow taken care of by the kernel? Help appreciated!

QnA
  • 1,035
  • 10
  • 25
  • 1
    The file descriptor is not passed in literal numeric form; instead, the kernel automatically duplicates the fd from the calling process into a (presumable next) open slot on the receiving end, so the sending and receiving file descriptors won't have the same numeric value unless by coincidence. These are not generic binary messages ("here are some bytes), but the oddly-named `SCM_RIGHTS` value invites the kernel to do the special FD magic. – Steve Friedl Jun 01 '20 at 19:58

1 Answers1

3

Expanding on the whole answer:

1) When you pass a file descriptor over a UNIX domain socket, the message structure - which includes the SCM_RIGHTS token, instructs the kernel to duplicate the file descriptor on the fly as it passes through the socket, magically emerging on the other end with the FD value being the next available slot.

As you suggested in your question, it couldn't possibly work if the FD were passed literally ("hey other end, here is FD#3 to do stuff with, good luck with that") and that FD was already in use. The dup-like behavior is what makes the FD usable on the other end.

The only time the FDs will match on both ends is by coincidence.

2) I don't know how the kernel actually handles ref counts, but I'm confident that it's treated as a dup operation, so passing from one end to another means that two processes now have the file open, and this isn't any kind of special case.

When one end closes the file, refcounts are handled as in any other kind of dup situation.

Steve Friedl
  • 3,929
  • 1
  • 23
  • 30
  • Thanks! I revisited the Linux kernel code, SCM_RIGHTS is indeed one of the two special control message types that are dealt with in the kernel _scm_send() function (the other is SCM_CREDENTIALS), which is called by both STREAM and DGRAM type unix sockets sendmsg funcs. What happens in Linux's case, is that the address of file object is indirectly put into the control block of sk_buff. The sender side also bumped up the refcount of the file. Receiver grabs this file pointer and installs it into a new unused fd. – QnA Jun 01 '20 at 23:10
  • So, sending descriptors across processes in unix domain sockets is part of the POSIX standard, rather than an adhoc usage of unix socket I originally thought it was. – QnA Jun 01 '20 at 23:10
  • Those two aren't mutually exclusive :-) I think it was probably built on an ad-hoc basis to solve some problem a long time ago, but proved useful enough to be posixified. Too bad we don't have 50 years of git history for *ix – Steve Friedl Jun 01 '20 at 23:26