From aefad9593ec5ad4aae5346253a8b646364cd7317 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 22 Mar 2018 20:52:43 -0500 Subject: sem/security: Pass kern_ipc_perm not sem_array into the sem security hooks All of the implementations of security hooks that take sem_array only access sem_perm the struct kern_ipc_perm member. This means the dependencies of the sem security hooks can be simplified by passing the kern_ipc_perm member of sem_array. Making this change will allow struct sem and struct sem_array to become private to ipc/sem.c. Signed-off-by: "Eric W. Biederman" --- ipc/sem.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'ipc/sem.c') diff --git a/ipc/sem.c b/ipc/sem.c index a4af04979fd2..01f5c63670ae 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -265,7 +265,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 +495,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; @@ -535,10 +535,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params) */ 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); + return security_sem_associate(ipcp, semflg); } /* @@ -1209,7 +1206,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; @@ -1300,7 +1297,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; @@ -1354,7 +1351,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; @@ -1545,7 +1542,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; @@ -1962,7 +1959,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; -- cgit v1.2.3-59-g8ed1b From 1a5c1349d105df5196ad9025e271b02a4dc05aee Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 22 Mar 2018 21:30:56 -0500 Subject: sem: Move struct sem and struct sem_array into ipc/sem.c All of the users are now in ipc/sem.c so make the definitions local to that file to make code maintenance easier. AKA to prevent rebuilding the entire kernel when one of these files is changed. Signed-off-by: "Eric W. Biederman" --- include/linux/sem.h | 40 +--------------------------------------- ipc/sem.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 39 deletions(-) (limited to 'ipc/sem.c') diff --git a/include/linux/sem.h b/include/linux/sem.h index 9badd322dcee..5608a500c43e 100644 --- a/include/linux/sem.h +++ b/include/linux/sem.h @@ -2,48 +2,10 @@ #ifndef _LINUX_SEM_H #define _LINUX_SEM_H -#include -#include -#include -#include #include struct task_struct; - -/* 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). - */ - int 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; +struct sem_undo_list; #ifdef CONFIG_SYSVIPC diff --git a/ipc/sem.c b/ipc/sem.c index 01f5c63670ae..d661c491b0a5 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -88,6 +88,40 @@ #include #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). + */ + int 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 { -- cgit v1.2.3-59-g8ed1b From 51d6f2635b39709ee5e62479be23d423b760292c Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 23 Mar 2018 01:11:29 -0500 Subject: ipc/sem: Fix semctl(..., GETPID, ...) between pid namespaces Today the last process to update a semaphore is remembered and reported in the pid namespace of that process. If there are processes in any other pid namespace querying that process id with GETPID the result will be unusable nonsense as it does not make any sense in your own pid namespace. Due to ipc_update_pid I don't think you will be able to get System V ipc semaphores into a troublesome cache line ping-pong. Using struct pids from separate process are not a problem because they do not share a cache line. Using struct pid from different threads of the same process are unlikely to be a problem as the reference count update can be avoided. Further linux futexes are a much better tool for the job of mutual exclusion between processes than System V semaphores. So I expect programs that are performance limited by their interprocess mutual exclusion primitive will be using futexes. So while it is possible that enhancing the storage of the last rocess of a System V semaphore from an integer to a struct pid will cause a performance regression because of the effect of frequently updating the pid reference count. I don't expect that to happen in practice. This change updates semctl(..., GETPID, ...) to return the process id of the last process to update a semphore inthe pid namespace of the calling process. Fixes: b488893a390e ("pid namespaces: changes to show virtual ids to user") Signed-off-by: "Eric W. Biederman" --- ipc/sem.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'ipc/sem.c') diff --git a/ipc/sem.c b/ipc/sem.c index d661c491b0a5..47b263960524 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -98,7 +98,7 @@ struct sem { * - semctl, via SETVAL and SETALL. * - at task exit when performing undo adjustments (see exit_sem). */ - int sempid; + struct pid *sempid; spinlock_t lock; /* spinlock for fine-grained semtimedop */ struct list_head pending_alter; /* pending single-sop operations */ /* that alter the semaphore */ @@ -128,7 +128,7 @@ 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 */ @@ -628,7 +628,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; @@ -666,7 +667,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--; } @@ -753,7 +754,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; @@ -1160,6 +1161,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 */ @@ -1352,7 +1354,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); @@ -1473,7 +1475,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); @@ -1505,7 +1507,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); @@ -2024,7 +2026,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; @@ -2318,7 +2320,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 */ -- cgit v1.2.3-59-g8ed1b From 50ab44b1c5d1b13305ce8acb74c8e50e0dcbaedc Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 23 Mar 2018 23:41:55 -0500 Subject: ipc: Directly call the security hook in ipc_ops.associate After the last round of cleanups the shm, sem, and msg associate operations just became trivial wrappers around the appropriate security method. Simplify things further by just calling the security method directly. Signed-off-by: "Eric W. Biederman" --- ipc/msg.c | 10 +--------- ipc/sem.c | 10 +--------- ipc/shm.c | 10 +--------- 3 files changed, 3 insertions(+), 27 deletions(-) (limited to 'ipc/sem.c') diff --git a/ipc/msg.c b/ipc/msg.c index 825ad585a6ff..d667dd8e97ab 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -272,20 +272,12 @@ static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) ipc_rcu_putref(&msq->q_perm, msg_rcu_free); } -/* - * Called with msg_ids.rwsem and ipcp locked. - */ -static inline int msg_security(struct kern_ipc_perm *ipcp, int msgflg) -{ - return security_msg_queue_associate(ipcp, msgflg); -} - SYSCALL_DEFINE2(msgget, key_t, key, int, msgflg) { struct ipc_namespace *ns; static const struct ipc_ops msg_ops = { .getnew = newque, - .associate = msg_security, + .associate = security_msg_queue_associate, }; struct ipc_params msg_params; diff --git a/ipc/sem.c b/ipc/sem.c index 47b263960524..09d54af076a4 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -564,14 +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) -{ - return security_sem_associate(ipcp, semflg); -} - /* * Called with sem_ids.rwsem and ipcp locked. */ @@ -592,7 +584,7 @@ SYSCALL_DEFINE3(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; diff --git a/ipc/shm.c b/ipc/shm.c index 932b7e411c6c..018db3d0e70e 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -656,14 +656,6 @@ no_file: return error; } -/* - * Called with shm_ids.rwsem and ipcp locked. - */ -static inline int shm_security(struct kern_ipc_perm *ipcp, int shmflg) -{ - return security_shm_associate(ipcp, shmflg); -} - /* * Called with shm_ids.rwsem and ipcp locked. */ @@ -684,7 +676,7 @@ SYSCALL_DEFINE3(shmget, key_t, key, size_t, size, int, shmflg) struct ipc_namespace *ns; static const struct ipc_ops shm_ops = { .getnew = newseg, - .associate = shm_security, + .associate = security_shm_associate, .more_checks = shm_more_checks, }; struct ipc_params shm_params; -- cgit v1.2.3-59-g8ed1b