Commit ec1b915e authored by David Howells's avatar David Howells Committed by Linus Torvalds

[PATCH] FRV: Semaphore implementation race fix

This fixes a race in the FRV arch's semaphore implementation.  The same
type of fixes were applied to the rw-semaphore implementations to fix
the same races there.

The race involved the on-stack record linked into the semaphore's queue
by the down() executed by a process now sleeping on the semaphore going
away and the sleeping task going away before the process that woke it up
during up() processing had finished with those structures.
Signed-Off-By: default avatarDavid Howells <dhowells@redhat.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent c5fe4ed6
...@@ -43,17 +43,18 @@ void __down(struct semaphore *sem, unsigned long flags) ...@@ -43,17 +43,18 @@ void __down(struct semaphore *sem, unsigned long flags)
struct task_struct *tsk = current; struct task_struct *tsk = current;
struct sem_waiter waiter; struct sem_waiter waiter;
semtrace(sem,"Entering __down"); semtrace(sem, "Entering __down");
/* set up my own style of waitqueue */ /* set up my own style of waitqueue */
waiter.task = tsk; waiter.task = tsk;
get_task_struct(tsk);
list_add_tail(&waiter.list, &sem->wait_list); list_add_tail(&waiter.list, &sem->wait_list);
/* we don't need to touch the semaphore struct anymore */ /* we don't need to touch the semaphore struct anymore */
spin_unlock_irqrestore(&sem->wait_lock, flags); spin_unlock_irqrestore(&sem->wait_lock, flags);
/* wait to be given the lock */ /* wait to be given the semaphore */
set_task_state(tsk, TASK_UNINTERRUPTIBLE); set_task_state(tsk, TASK_UNINTERRUPTIBLE);
for (;;) { for (;;) {
...@@ -64,7 +65,7 @@ void __down(struct semaphore *sem, unsigned long flags) ...@@ -64,7 +65,7 @@ void __down(struct semaphore *sem, unsigned long flags)
} }
tsk->state = TASK_RUNNING; tsk->state = TASK_RUNNING;
semtrace(sem,"Leaving __down"); semtrace(sem, "Leaving __down");
} }
EXPORT_SYMBOL(__down); EXPORT_SYMBOL(__down);
...@@ -83,6 +84,7 @@ int __down_interruptible(struct semaphore *sem, unsigned long flags) ...@@ -83,6 +84,7 @@ int __down_interruptible(struct semaphore *sem, unsigned long flags)
/* set up my own style of waitqueue */ /* set up my own style of waitqueue */
waiter.task = tsk; waiter.task = tsk;
get_task_struct(tsk);
list_add_tail(&waiter.list, &sem->wait_list); list_add_tail(&waiter.list, &sem->wait_list);
...@@ -91,7 +93,7 @@ int __down_interruptible(struct semaphore *sem, unsigned long flags) ...@@ -91,7 +93,7 @@ int __down_interruptible(struct semaphore *sem, unsigned long flags)
spin_unlock_irqrestore(&sem->wait_lock, flags); spin_unlock_irqrestore(&sem->wait_lock, flags);
/* wait to be given the lock */ /* wait to be given the semaphore */
ret = 0; ret = 0;
for (;;) { for (;;) {
if (list_empty(&waiter.list)) if (list_empty(&waiter.list))
...@@ -116,6 +118,8 @@ int __down_interruptible(struct semaphore *sem, unsigned long flags) ...@@ -116,6 +118,8 @@ int __down_interruptible(struct semaphore *sem, unsigned long flags)
} }
spin_unlock_irqrestore(&sem->wait_lock, flags); spin_unlock_irqrestore(&sem->wait_lock, flags);
if (ret == -EINTR)
put_task_struct(current);
goto out; goto out;
} }
...@@ -127,14 +131,24 @@ EXPORT_SYMBOL(__down_interruptible); ...@@ -127,14 +131,24 @@ EXPORT_SYMBOL(__down_interruptible);
*/ */
void __up(struct semaphore *sem) void __up(struct semaphore *sem)
{ {
struct task_struct *tsk;
struct sem_waiter *waiter; struct sem_waiter *waiter;
semtrace(sem,"Entering __up"); semtrace(sem,"Entering __up");
/* grant the token to the process at the front of the queue */ /* grant the token to the process at the front of the queue */
waiter = list_entry(sem->wait_list.next, struct sem_waiter, list); waiter = list_entry(sem->wait_list.next, struct sem_waiter, list);
/* We must be careful not to touch 'waiter' after we set ->task = NULL.
* It is an allocated on the waiter's stack and may become invalid at
* any time after that point (due to a wakeup from another source).
*/
list_del_init(&waiter->list); list_del_init(&waiter->list);
wake_up_process(waiter->task); tsk = waiter->task;
mb();
waiter->task = NULL;
wake_up_process(tsk);
put_task_struct(tsk);
semtrace(sem,"Leaving __up"); semtrace(sem,"Leaving __up");
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment