From 3c24783bb2deafaa106b7e69a97540071afc590c Mon Sep 17 00:00:00 2001 From: Manfred Spraul Date: Wed, 2 Nov 2011 13:38:50 -0700 Subject: ipc/sem.c: fix return code race with semop vs. semop +semctl(IPC_RMID) sys_semtimedop() may return -EIDRM although the semaphore operation completed successfully: thread 1: thread 2: semtimedop(), sleeps semop(): * acquires sem_lock() semtimedop() woken up due to timeout sem_lock() loops * notices that thread 2 could be completed. * performs the operations that thread 2 is sleeping on. * marks the semaphore operation as IN_WAKEUP * drops sem_lock(), does wakeup, sets return code to 0 * thread delayed due to interrupt, whatever * returns to user space * thread still delayed semctl(IPC_RMID) * acquires sem_lock() * ipc_rmid(), ipcp->deleted=1 * drops sem_lock() * thread finally continues - but seem_lock() now fails due to ipcp->deleted == 1 * returns -EIDRM instead of 0 The fix is trivial: Always use the return code in queue.status. In real world, the race probably doesn't matter: If the semaphore array is destroyed, the app is probably not interested if the last operation succeeded or was already cancelled. Signed-off-by: Manfred Spraul Cc: Thomas Gleixner Cc: Mike Galbraith Acked-by: Peter Zijlstra Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- ipc/sem.c | 1 - 1 file changed, 1 deletion(-) (limited to 'ipc') diff --git a/ipc/sem.c b/ipc/sem.c index c8e00f8b4be..fb13be17945 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -1460,7 +1460,6 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, * Array removed? If yes, leave without sem_unlock(). */ if (IS_ERR(sma)) { - error = -EIDRM; goto out_free; } -- cgit v1.2.3-70-g09d2 From 0b0577f6080c0645b079dcc03fdbaf40d928beb8 Mon Sep 17 00:00:00 2001 From: Manfred Spraul Date: Wed, 2 Nov 2011 13:38:52 -0700 Subject: ipc/sem.c: handle spurious wakeups semtimedop() does not handle spurious wakeups, it returns -EINTR to user space. Most other schedule() users would just loop and not return to user space. The patch adds such a loop to semtimedop() Signed-off-by: Manfred Spraul Reported-by: Peter Zijlstra Acked-by: Peter Zijlstra Cc: Thomas Gleixner Cc: Mike Galbraith Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- ipc/sem.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'ipc') diff --git a/ipc/sem.c b/ipc/sem.c index fb13be17945..227948f28ce 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -1426,6 +1426,8 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, queue.status = -EINTR; queue.sleeper = current; + +sleep_again: current->state = TASK_INTERRUPTIBLE; sem_unlock(sma); @@ -1478,6 +1480,13 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, */ if (timeout && jiffies_left == 0) error = -EAGAIN; + + /* + * If the wakeup was spurious, just retry + */ + if (error == -EINTR && !signal_pending(current)) + goto sleep_again; + unlink_queue(sma, &queue); out_unlock_free: -- cgit v1.2.3-70-g09d2 From e57940d719e9fc5223d133b631f8cb5232d6064e Mon Sep 17 00:00:00 2001 From: Manfred Spraul Date: Wed, 2 Nov 2011 13:38:54 -0700 Subject: ipc/sem.c: remove private structures from public header file include/linux/sem.h contains several structures that are only used within ipc/sem.c. The patch moves them into ipc/sem.c - there is no need to expose the structures to the whole kernel. No functional changes, only whitespace cleanups and 80-char per line fixes. Signed-off-by: Manfred Spraul Acked-by: Peter Zijlstra Cc: Thomas Gleixner Cc: Mike Galbraith Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sem.h | 42 ------------------------------------------ ipc/sem.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 42 deletions(-) (limited to 'ipc') diff --git a/include/linux/sem.h b/include/linux/sem.h index 1feb2de2ee5..464842621a4 100644 --- a/include/linux/sem.h +++ b/include/linux/sem.h @@ -83,13 +83,6 @@ struct seminfo { struct task_struct; -/* One semaphore structure for each semaphore in the system. */ -struct sem { - int semval; /* current value */ - int sempid; /* pid of last operation */ - struct list_head sem_pending; /* pending single-sop operations */ -}; - /* One sem_array data structure for each set of semaphores in the system. */ struct sem_array { struct kern_ipc_perm ____cacheline_aligned_in_smp @@ -103,41 +96,6 @@ struct sem_array { int complex_count; /* pending complex operations */ }; -/* One queue for each sleeping process in the system. */ -struct sem_queue { - struct list_head simple_list; /* queue of pending operations */ - 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 */ - int status; /* completion status of operation */ - struct sembuf *sops; /* array of pending operations */ - int nsops; /* number of operations */ - int alter; /* does the operation alter the array? */ -}; - -/* Each task has a list of undo requests. They are executed automatically - * when the process exits. - */ -struct sem_undo { - struct list_head list_proc; /* per-process list: all undos from one process. */ - /* rcu protected */ - struct rcu_head rcu; /* rcu struct for sem_undo() */ - struct sem_undo_list *ulp; /* sem_undo_list for the process */ - struct list_head list_id; /* per semaphore array list: all undos for one array */ - int semid; /* semaphore set identifier */ - short * semadj; /* array of adjustments, one per semaphore */ -}; - -/* sem_undo_list controls shared access to the list of sem_undo structures - * that may be shared among all a CLONE_SYSVSEM task group. - */ -struct sem_undo_list { - atomic_t refcnt; - spinlock_t lock; - struct list_head list_proc; -}; - struct sysv_sem { struct sem_undo_list *undo_list; }; diff --git a/ipc/sem.c b/ipc/sem.c index 227948f28ce..5215a81420d 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -90,6 +90,52 @@ #include #include "util.h" +/* One semaphore structure for each semaphore in the system. */ +struct sem { + int semval; /* current value */ + int sempid; /* pid of last operation */ + struct list_head sem_pending; /* pending single-sop operations */ +}; + +/* One queue for each sleeping process in the system. */ +struct sem_queue { + struct list_head simple_list; /* queue of pending operations */ + 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 */ + int status; /* completion status of operation */ + struct sembuf *sops; /* array of pending operations */ + int nsops; /* number of operations */ + int alter; /* does *sops alter the array? */ +}; + +/* Each task has a list of undo requests. They are executed automatically + * when the process exits. + */ +struct sem_undo { + struct list_head list_proc; /* per-process list: * + * all undos from one process + * rcu protected */ + struct rcu_head rcu; /* rcu struct for sem_undo */ + struct sem_undo_list *ulp; /* back ptr to sem_undo_list */ + struct list_head list_id; /* per semaphore array list: + * all undos for one array */ + int semid; /* semaphore set identifier */ + short *semadj; /* array of adjustments */ + /* one per semaphore */ +}; + +/* sem_undo_list controls shared access to the list of sem_undo structures + * that may be shared among all a CLONE_SYSVSEM task group. + */ +struct sem_undo_list { + atomic_t refcnt; + spinlock_t lock; + struct list_head list_proc; +}; + + #define sem_ids(ns) ((ns)->ids[IPC_SEM_IDS]) #define sem_unlock(sma) ipc_unlock(&(sma)->sem_perm) -- cgit v1.2.3-70-g09d2