diff options
| author | Oleg Nesterov <oleg@redhat.com> | 2013-04-30 18:28:20 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-04-30 20:04:07 -0400 |
| commit | e56fb2874015370e3b7f8d85051f6dce26051df9 (patch) | |
| tree | 0820c06eb4a25daf65734412af139e98ac1649c8 /include/linux | |
| parent | 12eaaf309a798973d215f7f21aa5a67a760ed7c8 (diff) | |
exec: do not abuse ->cred_guard_mutex in threadgroup_lock()
threadgroup_lock() takes signal->cred_guard_mutex to ensure that
thread_group_leader() is stable. This doesn't look nice, the scope of
this lock in do_execve() is huge.
And as Dave pointed out this can lead to deadlock, we have the
following dependencies:
do_execve: cred_guard_mutex -> i_mutex
cgroup_mount: i_mutex -> cgroup_mutex
attach_task_by_pid: cgroup_mutex -> cred_guard_mutex
Change de_thread() to take threadgroup_change_begin() around the
switch-the-leader code and change threadgroup_lock() to avoid
->cred_guard_mutex.
Note that de_thread() can't sleep with ->group_rwsem held, this can
obviously deadlock with the exiting leader if the writer is active, so it
does threadgroup_change_end() before schedule().
Reported-by: Dave Jones <davej@redhat.com>
Acked-by: Tejun Heo <tj@kernel.org>
Acked-by: Li Zefan <lizefan@huawei.com>
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'include/linux')
| -rw-r--r-- | include/linux/sched.h | 18 |
1 files changed, 4 insertions, 14 deletions
diff --git a/include/linux/sched.h b/include/linux/sched.h index a22baf83c20c..6f950048b6e9 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
| @@ -2249,27 +2249,18 @@ static inline void threadgroup_change_end(struct task_struct *tsk) | |||
| 2249 | * | 2249 | * |
| 2250 | * Lock the threadgroup @tsk belongs to. No new task is allowed to enter | 2250 | * Lock the threadgroup @tsk belongs to. No new task is allowed to enter |
| 2251 | * and member tasks aren't allowed to exit (as indicated by PF_EXITING) or | 2251 | * and member tasks aren't allowed to exit (as indicated by PF_EXITING) or |
| 2252 | * perform exec. This is useful for cases where the threadgroup needs to | 2252 | * change ->group_leader/pid. This is useful for cases where the threadgroup |
| 2253 | * stay stable across blockable operations. | 2253 | * needs to stay stable across blockable operations. |
| 2254 | * | 2254 | * |
| 2255 | * fork and exit paths explicitly call threadgroup_change_{begin|end}() for | 2255 | * fork and exit paths explicitly call threadgroup_change_{begin|end}() for |
| 2256 | * synchronization. While held, no new task will be added to threadgroup | 2256 | * synchronization. While held, no new task will be added to threadgroup |
| 2257 | * and no existing live task will have its PF_EXITING set. | 2257 | * and no existing live task will have its PF_EXITING set. |
| 2258 | * | 2258 | * |
| 2259 | * During exec, a task goes and puts its thread group through unusual | 2259 | * de_thread() does threadgroup_change_{begin|end}() when a non-leader |
| 2260 | * changes. After de-threading, exclusive access is assumed to resources | 2260 | * sub-thread becomes a new leader. |
| 2261 | * which are usually shared by tasks in the same group - e.g. sighand may | ||
| 2262 | * be replaced with a new one. Also, the exec'ing task takes over group | ||
| 2263 | * leader role including its pid. Exclude these changes while locked by | ||
| 2264 | * grabbing cred_guard_mutex which is used to synchronize exec path. | ||
| 2265 | */ | 2261 | */ |
| 2266 | static inline void threadgroup_lock(struct task_struct *tsk) | 2262 | static inline void threadgroup_lock(struct task_struct *tsk) |
| 2267 | { | 2263 | { |
| 2268 | /* | ||
| 2269 | * exec uses exit for de-threading nesting group_rwsem inside | ||
| 2270 | * cred_guard_mutex. Grab cred_guard_mutex first. | ||
| 2271 | */ | ||
| 2272 | mutex_lock(&tsk->signal->cred_guard_mutex); | ||
| 2273 | down_write(&tsk->signal->group_rwsem); | 2264 | down_write(&tsk->signal->group_rwsem); |
| 2274 | } | 2265 | } |
| 2275 | 2266 | ||
| @@ -2282,7 +2273,6 @@ static inline void threadgroup_lock(struct task_struct *tsk) | |||
| 2282 | static inline void threadgroup_unlock(struct task_struct *tsk) | 2273 | static inline void threadgroup_unlock(struct task_struct *tsk) |
| 2283 | { | 2274 | { |
| 2284 | up_write(&tsk->signal->group_rwsem); | 2275 | up_write(&tsk->signal->group_rwsem); |
| 2285 | mutex_unlock(&tsk->signal->cred_guard_mutex); | ||
| 2286 | } | 2276 | } |
| 2287 | #else | 2277 | #else |
| 2288 | static inline void threadgroup_change_begin(struct task_struct *tsk) {} | 2278 | static inline void threadgroup_change_begin(struct task_struct *tsk) {} |
