diff options
author | Mike Galbraith <efault@gmx.de> | 2010-12-16 09:09:52 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2011-01-04 09:10:34 -0500 |
commit | 4f8219875a0dad2cfad9e93a3fafcd9626db98d2 (patch) | |
tree | 050e09c752c2b0f6045f85247c64890cf5d4ec48 | |
parent | 6706125e291bd3dddd269e043323a6ab93ccd5fb (diff) |
sched, autogroup: Fix potential access to freed memory
Oleg pointed out that the /proc interface kref_get() useage may race with
the final put during autogroup_move_group(). A signal->autogroup assignment
may be in flight when the /proc interface dereference, leaving them taking
a reference to an already dead group.
Reported-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Mike Galbraith <efault@gmx.de>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
LKML-Reference: <1292508592.5940.28.camel@maggy.simson.net>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r-- | kernel/sched_autogroup.c | 25 |
1 files changed, 17 insertions, 8 deletions
diff --git a/kernel/sched_autogroup.c b/kernel/sched_autogroup.c index 57a7ac286a02..c80fedcd476b 100644 --- a/kernel/sched_autogroup.c +++ b/kernel/sched_autogroup.c | |||
@@ -41,6 +41,20 @@ static inline struct autogroup *autogroup_kref_get(struct autogroup *ag) | |||
41 | return ag; | 41 | return ag; |
42 | } | 42 | } |
43 | 43 | ||
44 | static inline struct autogroup *autogroup_task_get(struct task_struct *p) | ||
45 | { | ||
46 | struct autogroup *ag; | ||
47 | unsigned long flags; | ||
48 | |||
49 | if (!lock_task_sighand(p, &flags)) | ||
50 | return autogroup_kref_get(&autogroup_default); | ||
51 | |||
52 | ag = autogroup_kref_get(p->signal->autogroup); | ||
53 | unlock_task_sighand(p, &flags); | ||
54 | |||
55 | return ag; | ||
56 | } | ||
57 | |||
44 | static inline struct autogroup *autogroup_create(void) | 58 | static inline struct autogroup *autogroup_create(void) |
45 | { | 59 | { |
46 | struct autogroup *ag = kzalloc(sizeof(*ag), GFP_KERNEL); | 60 | struct autogroup *ag = kzalloc(sizeof(*ag), GFP_KERNEL); |
@@ -149,11 +163,7 @@ EXPORT_SYMBOL(sched_autogroup_detach); | |||
149 | 163 | ||
150 | void sched_autogroup_fork(struct signal_struct *sig) | 164 | void sched_autogroup_fork(struct signal_struct *sig) |
151 | { | 165 | { |
152 | struct task_struct *p = current; | 166 | sig->autogroup = autogroup_task_get(current); |
153 | |||
154 | spin_lock_irq(&p->sighand->siglock); | ||
155 | sig->autogroup = autogroup_kref_get(p->signal->autogroup); | ||
156 | spin_unlock_irq(&p->sighand->siglock); | ||
157 | } | 167 | } |
158 | 168 | ||
159 | void sched_autogroup_exit(struct signal_struct *sig) | 169 | void sched_autogroup_exit(struct signal_struct *sig) |
@@ -172,7 +182,6 @@ __setup("noautogroup", setup_autogroup); | |||
172 | 182 | ||
173 | #ifdef CONFIG_PROC_FS | 183 | #ifdef CONFIG_PROC_FS |
174 | 184 | ||
175 | /* Called with siglock held. */ | ||
176 | int proc_sched_autogroup_set_nice(struct task_struct *p, int *nice) | 185 | int proc_sched_autogroup_set_nice(struct task_struct *p, int *nice) |
177 | { | 186 | { |
178 | static unsigned long next = INITIAL_JIFFIES; | 187 | static unsigned long next = INITIAL_JIFFIES; |
@@ -194,7 +203,7 @@ int proc_sched_autogroup_set_nice(struct task_struct *p, int *nice) | |||
194 | return -EAGAIN; | 203 | return -EAGAIN; |
195 | 204 | ||
196 | next = HZ / 10 + jiffies; | 205 | next = HZ / 10 + jiffies; |
197 | ag = autogroup_kref_get(p->signal->autogroup); | 206 | ag = autogroup_task_get(p); |
198 | 207 | ||
199 | down_write(&ag->lock); | 208 | down_write(&ag->lock); |
200 | err = sched_group_set_shares(ag->tg, prio_to_weight[*nice + 20]); | 209 | err = sched_group_set_shares(ag->tg, prio_to_weight[*nice + 20]); |
@@ -209,7 +218,7 @@ int proc_sched_autogroup_set_nice(struct task_struct *p, int *nice) | |||
209 | 218 | ||
210 | void proc_sched_autogroup_show_task(struct task_struct *p, struct seq_file *m) | 219 | void proc_sched_autogroup_show_task(struct task_struct *p, struct seq_file *m) |
211 | { | 220 | { |
212 | struct autogroup *ag = autogroup_kref_get(p->signal->autogroup); | 221 | struct autogroup *ag = autogroup_task_get(p); |
213 | 222 | ||
214 | down_read(&ag->lock); | 223 | down_read(&ag->lock); |
215 | seq_printf(m, "/autogroup-%ld nice %d\n", ag->id, ag->nice); | 224 | seq_printf(m, "/autogroup-%ld nice %d\n", ag->id, ag->nice); |