diff options
author | Ingo Molnar <mingo@elte.hu> | 2005-08-05 23:05:27 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-08-05 15:56:41 -0700 |
commit | 00a5dfdb93f74e4d95fb0d83c890728e331f8810 (patch) | |
tree | f1d1043d7d02ad6bde3b158807b28bcfdafa21f6 | |
parent | ba02508248e90a9d696aebd18b48a3290235b53c (diff) | |
download | op-kernel-dev-00a5dfdb93f74e4d95fb0d83c890728e331f8810.zip op-kernel-dev-00a5dfdb93f74e4d95fb0d83c890728e331f8810.tar.gz |
[PATCH] Fix semundo lock leakage
semundo->lock can leak if semundo->refcount goes from 2 to 1 while
another thread has it locked. This causes major problems for PREEMPT
kernels.
The simplest fix for now is to undo the single-thread optimization.
This bug was found via relentless testing by Dominik Karall.
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | ipc/sem.c | 10 |
1 files changed, 3 insertions, 7 deletions
@@ -895,7 +895,7 @@ static inline void lock_semundo(void) struct sem_undo_list *undo_list; undo_list = current->sysvsem.undo_list; - if ((undo_list != NULL) && (atomic_read(&undo_list->refcnt) != 1)) + if (undo_list) spin_lock(&undo_list->lock); } @@ -915,7 +915,7 @@ static inline void unlock_semundo(void) struct sem_undo_list *undo_list; undo_list = current->sysvsem.undo_list; - if ((undo_list != NULL) && (atomic_read(&undo_list->refcnt) != 1)) + if (undo_list) spin_unlock(&undo_list->lock); } @@ -943,9 +943,7 @@ static inline int get_undo_list(struct sem_undo_list **undo_listp) if (undo_list == NULL) return -ENOMEM; memset(undo_list, 0, size); - /* don't initialize unodhd->lock here. It's done - * in copy_semundo() instead. - */ + spin_lock_init(&undo_list->lock); atomic_set(&undo_list->refcnt, 1); current->sysvsem.undo_list = undo_list; } @@ -1231,8 +1229,6 @@ int copy_semundo(unsigned long clone_flags, struct task_struct *tsk) error = get_undo_list(&undo_list); if (error) return error; - if (atomic_read(&undo_list->refcnt) == 1) - spin_lock_init(&undo_list->lock); atomic_inc(&undo_list->refcnt); tsk->sysvsem.undo_list = undo_list; } else |