diff options
| author | Bjoern B. Brandenburg <bbb@cs.unc.edu> | 2009-05-03 01:09:28 -0400 |
|---|---|---|
| committer | Bjoern B. Brandenburg <bbb@cs.unc.edu> | 2009-05-03 01:09:28 -0400 |
| commit | 490bfb44bf7f75bba7ad7a8dea89cb2eb2865fb7 (patch) | |
| tree | 74e359fd16e3e326c6ca91ece0b5460357081915 | |
| parent | 812251a109b77301a7d4376dc42ffc04c20f315c (diff) | |
rt domain: avoid use-after-free and re-init-while-in-use bugs
Don't just blindly overwrite the timers if they could
be still in use. The the use-after-free bug was observed
under Qemu.
| -rw-r--r-- | litmus/litmus.c | 17 | ||||
| -rw-r--r-- | litmus/rt_domain.c | 36 |
2 files changed, 34 insertions, 19 deletions
diff --git a/litmus/litmus.c b/litmus/litmus.c index 60e7507ba0..b286f8f013 100644 --- a/litmus/litmus.c +++ b/litmus/litmus.c | |||
| @@ -36,7 +36,7 @@ static LIST_HEAD(sched_sig_list); | |||
| 36 | static DEFINE_SPINLOCK(sched_sig_list_lock); | 36 | static DEFINE_SPINLOCK(sched_sig_list_lock); |
| 37 | 37 | ||
| 38 | static struct kmem_cache * heap_node_cache; | 38 | static struct kmem_cache * heap_node_cache; |
| 39 | static struct kmem_cache * release_heap_cache; | 39 | extern struct kmem_cache * release_heap_cache; |
| 40 | 40 | ||
| 41 | struct heap_node* heap_node_alloc(int gfp_flags) | 41 | struct heap_node* heap_node_alloc(int gfp_flags) |
| 42 | { | 42 | { |
| @@ -48,15 +48,8 @@ void heap_node_free(struct heap_node* hn) | |||
| 48 | kmem_cache_free(heap_node_cache, hn); | 48 | kmem_cache_free(heap_node_cache, hn); |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | struct release_heap* release_heap_alloc(int gfp_flags) | 51 | struct release_heap* release_heap_alloc(int gfp_flags); |
| 52 | { | 52 | void release_heap_free(struct release_heap* rh); |
| 53 | return kmem_cache_alloc(release_heap_cache, gfp_flags); | ||
| 54 | } | ||
| 55 | |||
| 56 | void release_heap_free(struct release_heap* rh) | ||
| 57 | { | ||
| 58 | kmem_cache_free(release_heap_cache, rh); | ||
| 59 | } | ||
| 60 | 53 | ||
| 61 | /* | 54 | /* |
| 62 | * sys_set_task_rt_param | 55 | * sys_set_task_rt_param |
| @@ -578,8 +571,8 @@ long litmus_admit_task(struct task_struct* tsk) | |||
| 578 | retval = -ENOMEM; | 571 | retval = -ENOMEM; |
| 579 | heap_node_free(tsk_rt(tsk)->heap_node); | 572 | heap_node_free(tsk_rt(tsk)->heap_node); |
| 580 | release_heap_free(tsk_rt(tsk)->rel_heap); | 573 | release_heap_free(tsk_rt(tsk)->rel_heap); |
| 581 | } else | 574 | } else |
| 582 | heap_node_init(&tsk_rt(tsk)->heap_node, tsk); | 575 | heap_node_init(&tsk_rt(tsk)->heap_node, tsk); |
| 583 | 576 | ||
| 584 | if (!retval) | 577 | if (!retval) |
| 585 | retval = litmus->admit_task(tsk); | 578 | retval = litmus->admit_task(tsk); |
diff --git a/litmus/rt_domain.c b/litmus/rt_domain.c index e02ec03ca1..0357df5f93 100644 --- a/litmus/rt_domain.c +++ b/litmus/rt_domain.c | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <linux/percpu.h> | 9 | #include <linux/percpu.h> |
| 10 | #include <linux/sched.h> | 10 | #include <linux/sched.h> |
| 11 | #include <linux/list.h> | 11 | #include <linux/list.h> |
| 12 | #include <linux/slab.h> | ||
| 12 | 13 | ||
| 13 | #include <litmus/litmus.h> | 14 | #include <litmus/litmus.h> |
| 14 | #include <litmus/sched_plugin.h> | 15 | #include <litmus/sched_plugin.h> |
| @@ -57,12 +58,39 @@ static enum hrtimer_restart on_release_timer(struct hrtimer *timer) | |||
| 57 | 58 | ||
| 58 | /* call release callback */ | 59 | /* call release callback */ |
| 59 | rh->dom->release_jobs(rh->dom, &rh->heap); | 60 | rh->dom->release_jobs(rh->dom, &rh->heap); |
| 61 | /* WARNING: rh can be referenced from other CPUs from now on. */ | ||
| 60 | 62 | ||
| 61 | TS_RELEASE_END; | 63 | TS_RELEASE_END; |
| 62 | 64 | ||
| 63 | return HRTIMER_NORESTART; | 65 | return HRTIMER_NORESTART; |
| 64 | } | 66 | } |
| 65 | 67 | ||
| 68 | /* allocated in litmus.c */ | ||
| 69 | struct kmem_cache * release_heap_cache; | ||
| 70 | |||
| 71 | struct release_heap* release_heap_alloc(int gfp_flags) | ||
| 72 | { | ||
| 73 | struct release_heap* rh; | ||
| 74 | rh= kmem_cache_alloc(release_heap_cache, gfp_flags); | ||
| 75 | if (rh) { | ||
| 76 | /* FIXME: could be a ctor */ | ||
| 77 | /* initialize timer */ | ||
| 78 | hrtimer_init(&rh->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); | ||
| 79 | rh->timer.function = on_release_timer; | ||
| 80 | #ifdef CONFIG_HIGH_RES_TIMERS | ||
| 81 | rh->timer.cb_mode = HRTIMER_CB_IRQSAFE_NO_RESTART; | ||
| 82 | #endif | ||
| 83 | } | ||
| 84 | return rh; | ||
| 85 | } | ||
| 86 | |||
| 87 | void release_heap_free(struct release_heap* rh) | ||
| 88 | { | ||
| 89 | /* make sure timer is no longer in use */ | ||
| 90 | hrtimer_cancel(&rh->timer); | ||
| 91 | kmem_cache_free(release_heap_cache, rh); | ||
| 92 | } | ||
| 93 | |||
| 66 | /* Caller most hold release lock. | 94 | /* Caller most hold release lock. |
| 67 | * Will return heap for given time. If no such heap exists prior to the invocation | 95 | * Will return heap for given time. If no such heap exists prior to the invocation |
| 68 | * it will be created. | 96 | * it will be created. |
| @@ -101,14 +129,8 @@ static struct release_heap* get_release_heap(rt_domain_t *rt, struct task_struct | |||
| 101 | rh->dom = rt; | 129 | rh->dom = rt; |
| 102 | heap_init(&rh->heap); | 130 | heap_init(&rh->heap); |
| 103 | 131 | ||
| 104 | /* initialize timer */ | ||
| 105 | hrtimer_init(&rh->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); | ||
| 106 | rh->timer.function = on_release_timer; | ||
| 107 | #ifdef CONFIG_HIGH_RES_TIMERS | ||
| 108 | rh->timer.cb_mode = HRTIMER_CB_IRQSAFE; | ||
| 109 | #endif | ||
| 110 | |||
| 111 | atomic_set(&rh->info.state, HRTIMER_START_ON_INACTIVE); | 132 | atomic_set(&rh->info.state, HRTIMER_START_ON_INACTIVE); |
| 133 | |||
| 112 | /* add to release queue */ | 134 | /* add to release queue */ |
| 113 | list_add(&rh->list, pos->prev); | 135 | list_add(&rh->list, pos->prev); |
| 114 | heap = rh; | 136 | heap = rh; |
