diff options
author | Ingo Molnar <mingo@elte.hu> | 2006-07-28 23:16:20 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-07-29 00:02:00 -0400 |
commit | 627371d73cdd04ed23fe098755b4f855138ad9e0 (patch) | |
tree | 2371684c61f91c39f562a6570784a1bae50f53a5 | |
parent | c97d20a6c51067a38f53680d9609b4cf2867d077 (diff) |
[PATCH] pi-futex: robust-futex exit crash fix
Fix pi_state->list handling bugs: list handling mishap, locking error.
Plus add more debug checks and fix a few style issues i noticed while
debugging this.
(reported by Ulrich Drepper and Jakub Jelinek.)
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | kernel/futex.c | 32 |
1 files changed, 24 insertions, 8 deletions
diff --git a/kernel/futex.c b/kernel/futex.c index cf0c8e21d1ab..f59003b1d8f9 100644 --- a/kernel/futex.c +++ b/kernel/futex.c | |||
@@ -415,15 +415,15 @@ out_unlock: | |||
415 | */ | 415 | */ |
416 | void exit_pi_state_list(struct task_struct *curr) | 416 | void exit_pi_state_list(struct task_struct *curr) |
417 | { | 417 | { |
418 | struct futex_hash_bucket *hb; | ||
419 | struct list_head *next, *head = &curr->pi_state_list; | 418 | struct list_head *next, *head = &curr->pi_state_list; |
420 | struct futex_pi_state *pi_state; | 419 | struct futex_pi_state *pi_state; |
420 | struct futex_hash_bucket *hb; | ||
421 | union futex_key key; | 421 | union futex_key key; |
422 | 422 | ||
423 | /* | 423 | /* |
424 | * We are a ZOMBIE and nobody can enqueue itself on | 424 | * We are a ZOMBIE and nobody can enqueue itself on |
425 | * pi_state_list anymore, but we have to be careful | 425 | * pi_state_list anymore, but we have to be careful |
426 | * versus waiters unqueueing themselfs | 426 | * versus waiters unqueueing themselves: |
427 | */ | 427 | */ |
428 | spin_lock_irq(&curr->pi_lock); | 428 | spin_lock_irq(&curr->pi_lock); |
429 | while (!list_empty(head)) { | 429 | while (!list_empty(head)) { |
@@ -431,21 +431,24 @@ void exit_pi_state_list(struct task_struct *curr) | |||
431 | next = head->next; | 431 | next = head->next; |
432 | pi_state = list_entry(next, struct futex_pi_state, list); | 432 | pi_state = list_entry(next, struct futex_pi_state, list); |
433 | key = pi_state->key; | 433 | key = pi_state->key; |
434 | hb = hash_futex(&key); | ||
434 | spin_unlock_irq(&curr->pi_lock); | 435 | spin_unlock_irq(&curr->pi_lock); |
435 | 436 | ||
436 | hb = hash_futex(&key); | ||
437 | spin_lock(&hb->lock); | 437 | spin_lock(&hb->lock); |
438 | 438 | ||
439 | spin_lock_irq(&curr->pi_lock); | 439 | spin_lock_irq(&curr->pi_lock); |
440 | /* | ||
441 | * We dropped the pi-lock, so re-check whether this | ||
442 | * task still owns the PI-state: | ||
443 | */ | ||
440 | if (head->next != next) { | 444 | if (head->next != next) { |
441 | spin_unlock(&hb->lock); | 445 | spin_unlock(&hb->lock); |
442 | continue; | 446 | continue; |
443 | } | 447 | } |
444 | 448 | ||
445 | list_del_init(&pi_state->list); | ||
446 | |||
447 | WARN_ON(pi_state->owner != curr); | 449 | WARN_ON(pi_state->owner != curr); |
448 | 450 | WARN_ON(list_empty(&pi_state->list)); | |
451 | list_del_init(&pi_state->list); | ||
449 | pi_state->owner = NULL; | 452 | pi_state->owner = NULL; |
450 | spin_unlock_irq(&curr->pi_lock); | 453 | spin_unlock_irq(&curr->pi_lock); |
451 | 454 | ||
@@ -470,7 +473,7 @@ lookup_pi_state(u32 uval, struct futex_hash_bucket *hb, struct futex_q *me) | |||
470 | head = &hb->chain; | 473 | head = &hb->chain; |
471 | 474 | ||
472 | list_for_each_entry_safe(this, next, head, list) { | 475 | list_for_each_entry_safe(this, next, head, list) { |
473 | if (match_futex (&this->key, &me->key)) { | 476 | if (match_futex(&this->key, &me->key)) { |
474 | /* | 477 | /* |
475 | * Another waiter already exists - bump up | 478 | * Another waiter already exists - bump up |
476 | * the refcount and return its pi_state: | 479 | * the refcount and return its pi_state: |
@@ -482,6 +485,8 @@ lookup_pi_state(u32 uval, struct futex_hash_bucket *hb, struct futex_q *me) | |||
482 | if (unlikely(!pi_state)) | 485 | if (unlikely(!pi_state)) |
483 | return -EINVAL; | 486 | return -EINVAL; |
484 | 487 | ||
488 | WARN_ON(!atomic_read(&pi_state->refcount)); | ||
489 | |||
485 | atomic_inc(&pi_state->refcount); | 490 | atomic_inc(&pi_state->refcount); |
486 | me->pi_state = pi_state; | 491 | me->pi_state = pi_state; |
487 | 492 | ||
@@ -510,6 +515,7 @@ lookup_pi_state(u32 uval, struct futex_hash_bucket *hb, struct futex_q *me) | |||
510 | pi_state->key = me->key; | 515 | pi_state->key = me->key; |
511 | 516 | ||
512 | spin_lock_irq(&p->pi_lock); | 517 | spin_lock_irq(&p->pi_lock); |
518 | WARN_ON(!list_empty(&pi_state->list)); | ||
513 | list_add(&pi_state->list, &p->pi_state_list); | 519 | list_add(&pi_state->list, &p->pi_state_list); |
514 | pi_state->owner = p; | 520 | pi_state->owner = p; |
515 | spin_unlock_irq(&p->pi_lock); | 521 | spin_unlock_irq(&p->pi_lock); |
@@ -584,9 +590,17 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_q *this) | |||
584 | if (curval != uval) | 590 | if (curval != uval) |
585 | return -EINVAL; | 591 | return -EINVAL; |
586 | 592 | ||
587 | list_del_init(&pi_state->owner->pi_state_list); | 593 | spin_lock_irq(&pi_state->owner->pi_lock); |
594 | WARN_ON(list_empty(&pi_state->list)); | ||
595 | list_del_init(&pi_state->list); | ||
596 | spin_unlock_irq(&pi_state->owner->pi_lock); | ||
597 | |||
598 | spin_lock_irq(&new_owner->pi_lock); | ||
599 | WARN_ON(!list_empty(&pi_state->list)); | ||
588 | list_add(&pi_state->list, &new_owner->pi_state_list); | 600 | list_add(&pi_state->list, &new_owner->pi_state_list); |
589 | pi_state->owner = new_owner; | 601 | pi_state->owner = new_owner; |
602 | spin_unlock_irq(&new_owner->pi_lock); | ||
603 | |||
590 | rt_mutex_unlock(&pi_state->pi_mutex); | 604 | rt_mutex_unlock(&pi_state->pi_mutex); |
591 | 605 | ||
592 | return 0; | 606 | return 0; |
@@ -1236,6 +1250,7 @@ static int do_futex_lock_pi(u32 __user *uaddr, int detect, int trylock, | |||
1236 | /* Owner died? */ | 1250 | /* Owner died? */ |
1237 | if (q.pi_state->owner != NULL) { | 1251 | if (q.pi_state->owner != NULL) { |
1238 | spin_lock_irq(&q.pi_state->owner->pi_lock); | 1252 | spin_lock_irq(&q.pi_state->owner->pi_lock); |
1253 | WARN_ON(list_empty(&q.pi_state->list)); | ||
1239 | list_del_init(&q.pi_state->list); | 1254 | list_del_init(&q.pi_state->list); |
1240 | spin_unlock_irq(&q.pi_state->owner->pi_lock); | 1255 | spin_unlock_irq(&q.pi_state->owner->pi_lock); |
1241 | } else | 1256 | } else |
@@ -1244,6 +1259,7 @@ static int do_futex_lock_pi(u32 __user *uaddr, int detect, int trylock, | |||
1244 | q.pi_state->owner = current; | 1259 | q.pi_state->owner = current; |
1245 | 1260 | ||
1246 | spin_lock_irq(¤t->pi_lock); | 1261 | spin_lock_irq(¤t->pi_lock); |
1262 | WARN_ON(!list_empty(&q.pi_state->list)); | ||
1247 | list_add(&q.pi_state->list, ¤t->pi_state_list); | 1263 | list_add(&q.pi_state->list, ¤t->pi_state_list); |
1248 | spin_unlock_irq(¤t->pi_lock); | 1264 | spin_unlock_irq(¤t->pi_lock); |
1249 | 1265 | ||