powerpc/spufs: reference context while dropping state mutex in scheduler

Based on an original patch from Christoph Hellwig <hch@lst.de>.

Currently, there is a possible reference-after-free in the spusched
code - contexts may be freed after we have released their state_mutex
in spusched_tick and find_victim.

This change takes a reference to the context before releasing the
mutex, so that the context doesn't get destroyed.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>

+8 -1
+8 -1
arch/powerpc/platforms/cell/spufs/sched.c
··· 641 642 if (tmp && tmp->prio > ctx->prio && 643 !(tmp->flags & SPU_CREATE_NOSCHED) && 644 - (!victim || tmp->prio > victim->prio)) 645 victim = spu->ctx; 646 } 647 mutex_unlock(&cbe_spu_info[node].list_mutex); 648 ··· 660 * look at another context or give up after X retries. 661 */ 662 if (!mutex_trylock(&victim->state_mutex)) { 663 victim = NULL; 664 goto restart; 665 } ··· 673 * restart the search. 674 */ 675 mutex_unlock(&victim->state_mutex); 676 victim = NULL; 677 goto restart; 678 } ··· 691 spu_add_to_rq(victim); 692 693 mutex_unlock(&victim->state_mutex); 694 695 return spu; 696 } ··· 990 struct spu_context *ctx = spu->ctx; 991 992 if (ctx) { 993 mutex_unlock(mtx); 994 spusched_tick(ctx); 995 mutex_lock(mtx); 996 } 997 } 998 mutex_unlock(mtx);
··· 641 642 if (tmp && tmp->prio > ctx->prio && 643 !(tmp->flags & SPU_CREATE_NOSCHED) && 644 + (!victim || tmp->prio > victim->prio)) { 645 victim = spu->ctx; 646 + get_spu_context(victim); 647 + } 648 } 649 mutex_unlock(&cbe_spu_info[node].list_mutex); 650 ··· 658 * look at another context or give up after X retries. 659 */ 660 if (!mutex_trylock(&victim->state_mutex)) { 661 + put_spu_context(victim); 662 victim = NULL; 663 goto restart; 664 } ··· 670 * restart the search. 671 */ 672 mutex_unlock(&victim->state_mutex); 673 + put_spu_context(victim); 674 victim = NULL; 675 goto restart; 676 } ··· 687 spu_add_to_rq(victim); 688 689 mutex_unlock(&victim->state_mutex); 690 + put_spu_context(victim); 691 692 return spu; 693 } ··· 985 struct spu_context *ctx = spu->ctx; 986 987 if (ctx) { 988 + get_spu_context(ctx); 989 mutex_unlock(mtx); 990 spusched_tick(ctx); 991 mutex_lock(mtx); 992 + put_spu_context(ctx); 993 } 994 } 995 mutex_unlock(mtx);