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; |