Skip to content

Day55 - SCM_RIGHTS / File Descriptor Passing

Goal

Learn how Linux transfers opened file descriptors between processes using:

  • AF_UNIX socket
  • sendmsg()
  • recvmsg()
  • ancillary data
  • SCM_RIGHTS

This day also demonstrates:

  • epoll on received fd
  • daemon / worker architecture
  • broker style resource delegation

Overview

Unlike normal IPC payload transfer, SCM_RIGHTS allows one process to pass an opened file descriptor to another process.

Example:

Parent:
    open("/dev/mypoll") -> fd=4

    send_fd(sock, 4)

Child:
    recv_fd(sock) -> fd=3

The fd numbers may differ between processes, but both refer to the same kernel file object.


Key Concept

SCM_RIGHTS does NOT send the fd integer itself.

Instead, the kernel:

  1. increases struct file reference count
  2. creates a new fd table entry in receiver process

Conceptually:

Parent fd table:
    fd=4
      |
      v
    struct file
      ^
      |
Child fd table:
    fd=3

This is similar to:

dup()

but across processes.


AF_UNIX Socket Types

SOCK_STREAM

Properties:

  • reliable
  • ordered
  • no message boundary

Multiple sendmsg() calls may merge into a single recvmsg().

Requires framing protocol.


SOCK_DGRAM

Properties:

  • preserves message boundary
  • datagram oriented

SOCK_SEQPACKET

Properties:

  • reliable
  • ordered
  • preserves message boundary

Very suitable for:

  • daemon IPC
  • command protocol
  • SCM_RIGHTS

Day55 uses:

SOCK_SEQPACKET

to simplify message handling.


sendmsg() / recvmsg()

struct msghdr

Describes a complete message.

Contains:

  • normal payload
  • ancillary data

struct iovec

Used for payload buffers.

Example:

iov.iov_base = buf;
iov.iov_len = len;

This is still copied through kernel space:

userspace
    |
copy_from_user()
    |
kernel socket buffer
    |
copy_to_user()
    |
receiver userspace

Ancillary Data

Ancillary data is attached control information.

Examples:

  • SCM_RIGHTS
  • SCM_CREDENTIALS
  • timestamps
  • packet metadata

struct cmsghdr

Control message header.

Example:

cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type  = SCM_RIGHTS;

Meaning:

  • socket layer control message
  • carrying fd passing payload

Important Macros

CMSG_SPACE()

Allocate enough memory for:

  • cmsghdr
  • payload
  • alignment padding

Example:

char ctrl[CMSG_SPACE(sizeof(int))];

CMSG_LEN()

Actual control message length.

Example:

cmsg->cmsg_len = CMSG_LEN(sizeof(int));

CMSG_DATA()

Returns pointer to ancillary payload region.

Example:

memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));

File Offset Sharing

SCM_RIGHTS shares the same open file description.

Therefore:

  • file offset is shared
  • file status flags are shared

Example:

Parent:
    lseek(fd, 8, SEEK_SET)

Child:
    read(received_fd)
        -> starts reading from offset 8

Open Mode Capability

Receiver cannot exceed original open mode.

Example:

Parent:
    open(path, O_RDONLY)

Child:
    write(received_fd)
        -> EBADF

SCM_RIGHTS transfers capability, but capability is limited by original open mode.


eventfd + epoll Demo

Day55 also demonstrated:

Parent:
    eventfd()
    send fd to child
    write(eventfd)

Child:
    recv fd
    epoll_ctl(received_fd)
    epoll_wait()
    read(eventfd)

This proves:

  • received fd behaves like a normal fd
  • epoll works on passed fd

/dev/mypoll Integration

Final demo:

Parent:
    open("/dev/mypoll")
    pass fd to child

Child:
    recv fd
    epoll_wait()
    read mypoll events

Result:

  • timer events received
  • manual trigger events received
  • peer disconnect handled through socket EPOLLHUP

Daemon Architecture Model

Day55 demonstrates a common Linux service design:

privileged daemon
    open restricted resource
        |
        +--> SCM_RIGHTS
                |
                v
         unprivileged worker

Examples in real systems:

  • systemd socket activation
  • Wayland
  • PipeWire
  • browser sandbox
  • media frameworks

Key Takeaways

  • SCM_RIGHTS transfers kernel file references
  • fd numbers are process-local
  • struct file is shared
  • epoll works normally on received fd
  • UNIX socket can act as fd broker
  • SOCK_SEQPACKET is excellent for structured IPC