Merge branch 'userns-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm...
[muen/linux.git] / ipc / sem.c
index 1cf56279a84c7f79cec2be8f11b9600f41249d34..2994da8ccc7f6d77b72451e96d6c7f4dcc0764c0 100644 (file)
--- a/ipc/sem.c
+++ b/ipc/sem.c
 #include <linux/uaccess.h>
 #include "util.h"
 
+/* One semaphore structure for each semaphore in the system. */
+struct sem {
+       int     semval;         /* current value */
+       /*
+        * PID of the process that last modified the semaphore. For
+        * Linux, specifically these are:
+        *  - semop
+        *  - semctl, via SETVAL and SETALL.
+        *  - at task exit when performing undo adjustments (see exit_sem).
+        */
+       struct pid *sempid;
+       spinlock_t      lock;   /* spinlock for fine-grained semtimedop */
+       struct list_head pending_alter; /* pending single-sop operations */
+                                       /* that alter the semaphore */
+       struct list_head pending_const; /* pending single-sop operations */
+                                       /* that do not alter the semaphore*/
+       time_t  sem_otime;      /* candidate for sem_otime */
+} ____cacheline_aligned_in_smp;
+
+/* One sem_array data structure for each set of semaphores in the system. */
+struct sem_array {
+       struct kern_ipc_perm    sem_perm;       /* permissions .. see ipc.h */
+       time64_t                sem_ctime;      /* create/last semctl() time */
+       struct list_head        pending_alter;  /* pending operations */
+                                               /* that alter the array */
+       struct list_head        pending_const;  /* pending complex operations */
+                                               /* that do not alter semvals */
+       struct list_head        list_id;        /* undo requests on this array */
+       int                     sem_nsems;      /* no. of semaphores in array */
+       int                     complex_count;  /* pending complex operations */
+       unsigned int            use_global_lock;/* >0: global lock required */
+
+       struct sem              sems[];
+} __randomize_layout;
 
 /* One queue for each sleeping process in the system. */
 struct sem_queue {
        struct list_head        list;    /* queue of pending operations */
        struct task_struct      *sleeper; /* this process */
        struct sem_undo         *undo;   /* undo structure */
-       int                     pid;     /* process id of requesting process */
+       struct pid              *pid;    /* process id of requesting process */
        int                     status;  /* completion status of operation */
        struct sembuf           *sops;   /* array of pending operations */
        struct sembuf           *blocking; /* the operation that blocked */
@@ -265,7 +299,7 @@ static void sem_rcu_free(struct rcu_head *head)
        struct kern_ipc_perm *p = container_of(head, struct kern_ipc_perm, rcu);
        struct sem_array *sma = container_of(p, struct sem_array, sem_perm);
 
-       security_sem_free(sma);
+       security_sem_free(&sma->sem_perm);
        kvfree(sma);
 }
 
@@ -495,7 +529,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
        sma->sem_perm.key = key;
 
        sma->sem_perm.security = NULL;
-       retval = security_sem_alloc(sma);
+       retval = security_sem_alloc(&sma->sem_perm);
        if (retval) {
                kvfree(sma);
                return retval;
@@ -530,17 +564,6 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
 }
 
 
-/*
- * Called with sem_ids.rwsem and ipcp locked.
- */
-static inline int sem_security(struct kern_ipc_perm *ipcp, int semflg)
-{
-       struct sem_array *sma;
-
-       sma = container_of(ipcp, struct sem_array, sem_perm);
-       return security_sem_associate(sma, semflg);
-}
-
 /*
  * Called with sem_ids.rwsem and ipcp locked.
  */
@@ -561,7 +584,7 @@ long ksys_semget(key_t key, int nsems, int semflg)
        struct ipc_namespace *ns;
        static const struct ipc_ops sem_ops = {
                .getnew = newary,
-               .associate = sem_security,
+               .associate = security_sem_associate,
                .more_checks = sem_more_checks,
        };
        struct ipc_params sem_params;
@@ -602,7 +625,8 @@ SYSCALL_DEFINE3(semget, key_t, key, int, nsems, int, semflg)
  */
 static int perform_atomic_semop_slow(struct sem_array *sma, struct sem_queue *q)
 {
-       int result, sem_op, nsops, pid;
+       int result, sem_op, nsops;
+       struct pid *pid;
        struct sembuf *sop;
        struct sem *curr;
        struct sembuf *sops;
@@ -640,7 +664,7 @@ static int perform_atomic_semop_slow(struct sem_array *sma, struct sem_queue *q)
        sop--;
        pid = q->pid;
        while (sop >= sops) {
-               sma->sems[sop->sem_num].sempid = pid;
+               ipc_update_pid(&sma->sems[sop->sem_num].sempid, pid);
                sop--;
        }
 
@@ -727,7 +751,7 @@ static int perform_atomic_semop(struct sem_array *sma, struct sem_queue *q)
                        un->semadj[sop->sem_num] = undo;
                }
                curr->semval += sem_op;
-               curr->sempid = q->pid;
+               ipc_update_pid(&curr->sempid, q->pid);
        }
 
        return 0;
@@ -1134,6 +1158,7 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
                        unlink_queue(sma, q);
                        wake_up_sem_queue_prepare(q, -EIDRM, &wake_q);
                }
+               ipc_update_pid(&sem->sempid, NULL);
        }
 
        /* Remove the semaphore set from the IDR */
@@ -1214,7 +1239,7 @@ static int semctl_stat(struct ipc_namespace *ns, int semid,
        if (ipcperms(ns, &sma->sem_perm, S_IRUGO))
                goto out_unlock;
 
-       err = security_sem_semctl(sma, cmd);
+       err = security_sem_semctl(&sma->sem_perm, cmd);
        if (err)
                goto out_unlock;
 
@@ -1305,7 +1330,7 @@ static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum,
                return -EACCES;
        }
 
-       err = security_sem_semctl(sma, SETVAL);
+       err = security_sem_semctl(&sma->sem_perm, SETVAL);
        if (err) {
                rcu_read_unlock();
                return -EACCES;
@@ -1326,7 +1351,7 @@ static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum,
                un->semadj[semnum] = 0;
 
        curr->semval = val;
-       curr->sempid = task_tgid_vnr(current);
+       ipc_update_pid(&curr->sempid, task_tgid(current));
        sma->sem_ctime = ktime_get_real_seconds();
        /* maybe some queued-up processes were waiting for this */
        do_smart_update(sma, NULL, 0, 0, &wake_q);
@@ -1359,7 +1384,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
        if (ipcperms(ns, &sma->sem_perm, cmd == SETALL ? S_IWUGO : S_IRUGO))
                goto out_rcu_wakeup;
 
-       err = security_sem_semctl(sma, cmd);
+       err = security_sem_semctl(&sma->sem_perm, cmd);
        if (err)
                goto out_rcu_wakeup;
 
@@ -1447,7 +1472,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
 
                for (i = 0; i < nsems; i++) {
                        sma->sems[i].semval = sem_io[i];
-                       sma->sems[i].sempid = task_tgid_vnr(current);
+                       ipc_update_pid(&sma->sems[i].sempid, task_tgid(current));
                }
 
                ipc_assert_locked_object(&sma->sem_perm);
@@ -1479,7 +1504,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
                err = curr->semval;
                goto out_unlock;
        case GETPID:
-               err = curr->sempid;
+               err = pid_vnr(curr->sempid);
                goto out_unlock;
        case GETNCNT:
                err = count_semcnt(sma, semnum, 0);
@@ -1550,7 +1575,7 @@ static int semctl_down(struct ipc_namespace *ns, int semid,
 
        sma = container_of(ipcp, struct sem_array, sem_perm);
 
-       err = security_sem_semctl(sma, cmd);
+       err = security_sem_semctl(&sma->sem_perm, cmd);
        if (err)
                goto out_unlock1;
 
@@ -1977,7 +2002,7 @@ static long do_semtimedop(int semid, struct sembuf __user *tsops,
                goto out_free;
        }
 
-       error = security_sem_semop(sma, sops, nsops, alter);
+       error = security_sem_semop(&sma->sem_perm, sops, nsops, alter);
        if (error) {
                rcu_read_unlock();
                goto out_free;
@@ -2008,7 +2033,7 @@ static long do_semtimedop(int semid, struct sembuf __user *tsops,
        queue.sops = sops;
        queue.nsops = nsops;
        queue.undo = un;
-       queue.pid = task_tgid_vnr(current);
+       queue.pid = task_tgid(current);
        queue.alter = alter;
        queue.dupsop = dupsop;
 
@@ -2315,7 +2340,7 @@ void exit_sem(struct task_struct *tsk)
                                        semaphore->semval = 0;
                                if (semaphore->semval > SEMVMX)
                                        semaphore->semval = SEMVMX;
-                               semaphore->sempid = task_tgid_vnr(current);
+                               ipc_update_pid(&semaphore->sempid, task_tgid(current));
                        }
                }
                /* maybe some queued-up processes were waiting for this */