IPC Data Structures & Permissions

 

IPC Data Structures & Permissions
Chapter 45, Section 45.3 | ipc_perm, Ownership & Access Control
ipc_perm
Permission struct
IPC_STAT
Read structure
IPC_SET
Modify structure
Key Terms
ipc_perm msqid_ds semid_ds shmid_ds IPC_STAT IPC_SET uid / gid cuid / cgid CAP_IPC_OWNER Permission mask EACCES EPERM

Associated Data Structures

For every System V IPC object created, the kernel maintains an associated data structure. This structure stores metadata about the object — including ownership, permissions, usage statistics, and the IPC key.

The exact form of this structure depends on the IPC mechanism:

  • Message queues → struct msqid_ds (in <sys/msg.h>)
  • Semaphores → struct semid_ds (in <sys/sem.h>)
  • Shared memory → struct shmid_ds (in <sys/shm.h>)

Despite their differences, all three structures share a common sub-structure called ipc_perm that holds ownership and permission information.

The ipc_perm Structure

The ipc_perm structure is embedded inside every IPC associated data structure. It is defined in <sys/ipc.h>:

struct ipc_perm {
    key_t          __key;   /* Key, as supplied to the 'get' call */
    uid_t          uid;     /* Owner's user ID   (changeable) */
    gid_t          gid;     /* Owner's group ID  (changeable) */
    uid_t          cuid;    /* Creator's user ID  (immutable) */
    gid_t          cgid;    /* Creator's group ID (immutable) */
    unsigned short mode;    /* Permissions bit mask */
    unsigned short __seq;   /* Sequence number (used in ID calculation) */
};

ipc_perm Fields — Who Does What
uid & gid
Current owner’s user/group ID. Can be changed via IPC_SET by privileged process or the creator.
cuid & cgid
Creator’s user/group ID. Set at object creation time and immutable — cannot be changed.
mode
9-bit permission mask (same bit positions as file permissions). Controls read/write for owner, group, other.
__seq
Sequence number used in IPC identifier calculation. Incremented each time an IPC object is created at this slot.
SUSv3 mandates: All fields shown above except __key and __seq are required by the standard. Both are provided by most UNIX implementations as well.

Reading and Modifying the Data Structure: IPC_STAT and IPC_SET

You interact with the associated data structure using the ctl calls (msgctl(), semctl(), shmctl()) with two generic operations:

  • IPC_STAT — copies the kernel’s associated data structure into a user-space buffer (requires read permission)
  • IPC_SET — updates selected fields in the kernel from the user-space buffer (requires ownership or privilege)
When initialized, uid, gid, cuid, cgid are all set to the effective user and group IDs of the calling process. The umask is NOT applied to IPC object permissions (unlike files).

Coding Example 1 — Reading IPC Data Structure (IPC_STAT)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    key_t key = 77777;
    int shmid;
    struct shmid_ds shmds;

    /* Create a shared memory segment */
    shmid = shmget(key, 4096, IPC_CREAT | 0664);
    if (shmid == -1) { perror("shmget"); exit(EXIT_FAILURE); }

    /* IPC_STAT: fetch the associated data structure from kernel */
    if (shmctl(shmid, IPC_STAT, &shmds) == -1) {
        perror("shmctl IPC_STAT");
        exit(EXIT_FAILURE);
    }

    /* Print permission info from the embedded ipc_perm structure */
    printf("--- Shared Memory Info (ID=%d) ---\n", shmid);
    printf("Key:           0x%x\n",   (unsigned int)shmds.shm_perm.__key);
    printf("Owner UID:     %d\n",     shmds.shm_perm.uid);
    printf("Owner GID:     %d\n",     shmds.shm_perm.gid);
    printf("Creator UID:   %d\n",     shmds.shm_perm.cuid);
    printf("Creator GID:   %d\n",     shmds.shm_perm.cgid);
    printf("Mode (octal):  %04o\n",   shmds.shm_perm.mode & 0777);
    printf("Segment size:  %zu bytes\n", shmds.shm_segsz);
    printf("Attached:      %lu processes\n", (unsigned long)shmds.shm_nattch);

    /* Cleanup */
    shmctl(shmid, IPC_RMID, NULL);
    return 0;
}

Coding Example 2 — Changing Ownership (IPC_SET)

The following shows how to change the owner UID of a shared memory segment. This requires that you are the creator, current owner, or have the CAP_SYS_ADMIN capability:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    key_t key = 88888;
    int shmid;
    struct shmid_ds shmds;
    uid_t new_uid = 1001;   /* Replace with actual UID on your system */

    /* Create shared memory */
    shmid = shmget(key, 4096, IPC_CREAT | 0600);
    if (shmid == -1) { perror("shmget"); exit(EXIT_FAILURE); }

    /* Step 1: Fetch current data structure from kernel */
    if (shmctl(shmid, IPC_STAT, &shmds) == -1) {
        perror("shmctl IPC_STAT");
        exit(EXIT_FAILURE);
    }
    printf("Before: Owner UID = %d\n", shmds.shm_perm.uid);

    /* Step 2: Modify the owner UID in our local copy */
    shmds.shm_perm.uid = new_uid;

    /* Step 3: Write the modified structure back to the kernel */
    if (shmctl(shmid, IPC_SET, &shmds) == -1) {
        perror("shmctl IPC_SET");
        exit(EXIT_FAILURE);
    }

    /* Step 4: Verify the change */
    if (shmctl(shmid, IPC_STAT, &shmds) == -1) {
        perror("shmctl IPC_STAT");
        exit(EXIT_FAILURE);
    }
    printf("After:  Owner UID = %d\n", shmds.shm_perm.uid);

    /* Cleanup */
    shmctl(shmid, IPC_RMID, NULL);
    return 0;
}
Note: Only the uid field (owner) is changeable. The cuid (creator) field is immutable — once set at creation time, it cannot be changed. You can also change gid and mode via IPC_SET.

IPC Object Permissions — How They Work

Like files, IPC permissions are divided into three categories: owner (user), group, and other. But there are important differences from file permissions:

IPC Permissions vs File Permissions
Aspect File Permissions IPC Permissions
Permission types read, write, execute read and write only (execute is ignored)
Check based on Process’s filesystem IDs Process’s effective user ID & effective/supplementary group IDs
Semaphore “write” N/A Commonly called “alter” permission
umask applied? Yes, on creation No — permissions set exactly as specified

Four-Step Permission Check Algorithm

When a process tries to access an IPC object, the kernel applies these checks in order:

1
Privileged process (CAP_IPC_OWNER): All permissions granted immediately. No further checks.
2
Owner match: If effective UID matches either uid (owner) or cuid (creator), grant owner permissions.
3
Group match: If effective GID or any supplementary GID matches gid (owner group) or cgid (creator group), grant group permissions.
4
Other: Grant other permissions from the mode field.
Smart optimization: The privilege check (step 1) is performed only if none of the other tests grant the needed permissions. This avoids unnecessarily setting the ASU process accounting flag.

What Permission is Required for Each Operation?
Operation Required Permission
Read a message / get semaphore value / attach shm for reading Read permission
Write a message / change semaphore / attach shm for writing Write permission
IPC_STAT (fetch data structure) Read permission
IPC_RMID (delete object) Privileged (CAP_SYS_ADMIN) OR effective UID = owner UID or creator UID
IPC_SET (change data structure) Privileged (CAP_SYS_ADMIN) OR effective UID = owner UID or creator UID
Execute permission is meaningless for IPC objects and is normally ignored — with one exception: if you request execute permission in a get call for an existing object, the kernel checks if it is granted (this is the only case where execute is checked).

Coding Example 3 — Permission Denied Scenario

This example demonstrates what happens when a second process tries to write to a queue that only allows read access for the group:

/* --- Process A (queue owner): creates queue with rw-r----- ---*/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

struct mymsg { long mtype; char mtext[64]; };

int main(void) {
    key_t key = 11111;
    int msqid;

    /* Permissions: owner rw, group r, others nothing
       S_IRUSR | S_IWUSR | S_IRGRP  = 0640 */
    msqid = msgget(key, IPC_CREAT | S_IRUSR | S_IWUSR | S_IRGRP);
    if (msqid == -1) { perror("msgget"); exit(EXIT_FAILURE); }
    printf("Queue created with perms 0640, ID=%d\n", msqid);

    /* --- Process B (same group, different user): tries to write ---
       A user in the group tries to open the queue with write permission */
    int id2 = msgget(key, S_IRUSR | S_IWUSR);   /* requests owner-rw */
    if (id2 == -1) {
        if (errno == EACCES) {
            printf("Access denied! Caller is not the owner.\n");
        } else {
            perror("msgget");
        }
    }

    /* Correct way for a group member: open with 0 flags
       (permission check happens at operation time, not get() time) */
    int id3 = msgget(key, 0);
    if (id3 != -1) {
        printf("Opened queue (no flags). ID=%d\n", id3);

        struct mymsg msg = { .mtype = 1 };
        snprintf(msg.mtext, sizeof(msg.mtext), "test");
        /* This will fail at runtime with EACCES because group has no write perm */
        if (msgsnd(id3, &msg, sizeof(msg.mtext), 0) == -1) {
            if (errno == EACCES)
                printf("msgsnd denied — group has read-only permission.\n");
            else
                perror("msgsnd");
        }
    }

    /* Cleanup */
    msgctl(msqid, IPC_RMID, NULL);
    return 0;
}
Key insight: A process can bypass the initial permission check in msgget() by passing 0 as the second argument. The actual permission denial then happens at the operation level (msgsnd/msgrcv).

🎯 Interview Questions — Data Structures & Permissions

Q1. What is the ipc_perm structure and where is it found?
A: ipc_perm is a sub-structure embedded in every IPC associated data structure (msqid_ds, semid_ds, shmid_ds). It holds the key, owner/creator user/group IDs, permission bits, and sequence number.
Q2. What is the difference between uid/gid and cuid/cgid in ipc_perm?
A: uid/gid represent the current owner IDs and can be changed via IPC_SET. cuid/cgid represent the creator IDs, are set at creation time from the effective IDs of the creating process, and are immutable — they cannot be changed.
Q3. Is the umask applied when setting permissions on a new IPC object?
A: No. The umask is NOT applied to IPC object permissions. The permissions are set exactly as specified in the flags argument to the get call. This is different from files, where umask masks out bits.
Q4. What operations are performed using IPC_STAT and IPC_SET?
A: IPC_STAT fetches a copy of the associated data structure from the kernel into a user-space variable (requires read permission). IPC_SET writes a modified version back to the kernel, updating fields like uid, gid, and mode (requires ownership or CAP_SYS_ADMIN).
Q5. What permissions does IPC_RMID (delete) require?
A: Neither read nor write permission is needed. Instead, the calling process must either have the CAP_SYS_ADMIN capability, or its effective UID must match either the owner UID or creator UID of the object. Otherwise EPERM is returned.
Q6. How do IPC permission checks differ from file permission checks on Linux?
A: File permission checks on Linux use the process’s filesystem IDs (fsuid/fsgid). IPC permission checks use the process’s effective user ID and effective/supplementary group IDs. Also, execute permission is meaningless for IPC objects.
Q7. What error is returned when a process is denied access to an IPC object?
A: EACCES is returned for most permission denials. EPERM is returned when a process tries to perform IPC_RMID or IPC_SET and does not have the required ownership or privileges.
Q8. Can a process that can no longer read an IPC object still change its permissions?
A: Yes. IPC_SET can still be used to change permissions even if the process cannot read the object (IPC_STAT requires read permission, but IPC_SET only requires ownership or privilege). This means a process could set permissions on an object that effectively locks it out from viewing via ipcs.

Leave a Reply

Your email address will not be published. Required fields are marked *