diff options
author | Cyrill Gorcunov <gorcunov@openvz.org> | 2012-05-17 20:03:25 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-17 21:00:51 -0400 |
commit | eb94cd96e05d6c65a07937e66a04ea265c1b767d (patch) | |
tree | 0432ec8db57798b5212fa2200fe1293b01bd3361 /fs/proc | |
parent | c0a5f4a05af588a0f9951f8d24e2564b09501918 (diff) |
fs, proc: fix ABBA deadlock in case of execution attempt of map_files/ entries
map_files/ entries are never supposed to be executed, still curious
minds might try to run them, which leads to the following deadlock
======================================================
[ INFO: possible circular locking dependency detected ]
3.4.0-rc4-24406-g841e6a6 #121 Not tainted
-------------------------------------------------------
bash/1556 is trying to acquire lock:
(&sb->s_type->i_mutex_key#8){+.+.+.}, at: do_lookup+0x267/0x2b1
but task is already holding lock:
(&sig->cred_guard_mutex){+.+.+.}, at: prepare_bprm_creds+0x2d/0x69
which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #1 (&sig->cred_guard_mutex){+.+.+.}:
validate_chain+0x444/0x4f4
__lock_acquire+0x387/0x3f8
lock_acquire+0x12b/0x158
__mutex_lock_common+0x56/0x3a9
mutex_lock_killable_nested+0x40/0x45
lock_trace+0x24/0x59
proc_map_files_lookup+0x5a/0x165
__lookup_hash+0x52/0x73
do_lookup+0x276/0x2b1
walk_component+0x3d/0x114
do_last+0xfc/0x540
path_openat+0xd3/0x306
do_filp_open+0x3d/0x89
do_sys_open+0x74/0x106
sys_open+0x21/0x23
tracesys+0xdd/0xe2
-> #0 (&sb->s_type->i_mutex_key#8){+.+.+.}:
check_prev_add+0x6a/0x1ef
validate_chain+0x444/0x4f4
__lock_acquire+0x387/0x3f8
lock_acquire+0x12b/0x158
__mutex_lock_common+0x56/0x3a9
mutex_lock_nested+0x40/0x45
do_lookup+0x267/0x2b1
walk_component+0x3d/0x114
link_path_walk+0x1f9/0x48f
path_openat+0xb6/0x306
do_filp_open+0x3d/0x89
open_exec+0x25/0xa0
do_execve_common+0xea/0x2f9
do_execve+0x43/0x45
sys_execve+0x43/0x5a
stub_execve+0x6c/0xc0
This is because prepare_bprm_creds grabs task->signal->cred_guard_mutex
and when do_lookup happens we try to grab task->signal->cred_guard_mutex
again in lock_trace.
Fix it using plain ptrace_may_access() helper in proc_map_files_lookup()
and in proc_map_files_readdir() instead of lock_trace(), the caller must
be CAP_SYS_ADMIN granted anyway.
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
Reported-by: Sasha Levin <levinsasha928@gmail.com>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Pavel Emelyanov <xemul@openvz.org>
Cc: Dave Jones <davej@redhat.com>
Cc: Vasiliy Kulikov <segoon@openwall.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/proc')
-rw-r--r-- | fs/proc/base.c | 20 |
1 files changed, 8 insertions, 12 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c index 1c8b280146d7..8e139c90f9fd 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -2177,16 +2177,16 @@ static struct dentry *proc_map_files_lookup(struct inode *dir, | |||
2177 | goto out; | 2177 | goto out; |
2178 | 2178 | ||
2179 | result = ERR_PTR(-EACCES); | 2179 | result = ERR_PTR(-EACCES); |
2180 | if (lock_trace(task)) | 2180 | if (!ptrace_may_access(task, PTRACE_MODE_READ)) |
2181 | goto out_put_task; | 2181 | goto out_put_task; |
2182 | 2182 | ||
2183 | result = ERR_PTR(-ENOENT); | 2183 | result = ERR_PTR(-ENOENT); |
2184 | if (dname_to_vma_addr(dentry, &vm_start, &vm_end)) | 2184 | if (dname_to_vma_addr(dentry, &vm_start, &vm_end)) |
2185 | goto out_unlock; | 2185 | goto out_put_task; |
2186 | 2186 | ||
2187 | mm = get_task_mm(task); | 2187 | mm = get_task_mm(task); |
2188 | if (!mm) | 2188 | if (!mm) |
2189 | goto out_unlock; | 2189 | goto out_put_task; |
2190 | 2190 | ||
2191 | down_read(&mm->mmap_sem); | 2191 | down_read(&mm->mmap_sem); |
2192 | vma = find_exact_vma(mm, vm_start, vm_end); | 2192 | vma = find_exact_vma(mm, vm_start, vm_end); |
@@ -2198,8 +2198,6 @@ static struct dentry *proc_map_files_lookup(struct inode *dir, | |||
2198 | out_no_vma: | 2198 | out_no_vma: |
2199 | up_read(&mm->mmap_sem); | 2199 | up_read(&mm->mmap_sem); |
2200 | mmput(mm); | 2200 | mmput(mm); |
2201 | out_unlock: | ||
2202 | unlock_trace(task); | ||
2203 | out_put_task: | 2201 | out_put_task: |
2204 | put_task_struct(task); | 2202 | put_task_struct(task); |
2205 | out: | 2203 | out: |
@@ -2233,7 +2231,7 @@ proc_map_files_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
2233 | goto out; | 2231 | goto out; |
2234 | 2232 | ||
2235 | ret = -EACCES; | 2233 | ret = -EACCES; |
2236 | if (lock_trace(task)) | 2234 | if (!ptrace_may_access(task, PTRACE_MODE_READ)) |
2237 | goto out_put_task; | 2235 | goto out_put_task; |
2238 | 2236 | ||
2239 | ret = 0; | 2237 | ret = 0; |
@@ -2241,12 +2239,12 @@ proc_map_files_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
2241 | case 0: | 2239 | case 0: |
2242 | ino = inode->i_ino; | 2240 | ino = inode->i_ino; |
2243 | if (filldir(dirent, ".", 1, 0, ino, DT_DIR) < 0) | 2241 | if (filldir(dirent, ".", 1, 0, ino, DT_DIR) < 0) |
2244 | goto out_unlock; | 2242 | goto out_put_task; |
2245 | filp->f_pos++; | 2243 | filp->f_pos++; |
2246 | case 1: | 2244 | case 1: |
2247 | ino = parent_ino(dentry); | 2245 | ino = parent_ino(dentry); |
2248 | if (filldir(dirent, "..", 2, 1, ino, DT_DIR) < 0) | 2246 | if (filldir(dirent, "..", 2, 1, ino, DT_DIR) < 0) |
2249 | goto out_unlock; | 2247 | goto out_put_task; |
2250 | filp->f_pos++; | 2248 | filp->f_pos++; |
2251 | default: | 2249 | default: |
2252 | { | 2250 | { |
@@ -2257,7 +2255,7 @@ proc_map_files_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
2257 | 2255 | ||
2258 | mm = get_task_mm(task); | 2256 | mm = get_task_mm(task); |
2259 | if (!mm) | 2257 | if (!mm) |
2260 | goto out_unlock; | 2258 | goto out_put_task; |
2261 | down_read(&mm->mmap_sem); | 2259 | down_read(&mm->mmap_sem); |
2262 | 2260 | ||
2263 | nr_files = 0; | 2261 | nr_files = 0; |
@@ -2287,7 +2285,7 @@ proc_map_files_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
2287 | flex_array_free(fa); | 2285 | flex_array_free(fa); |
2288 | up_read(&mm->mmap_sem); | 2286 | up_read(&mm->mmap_sem); |
2289 | mmput(mm); | 2287 | mmput(mm); |
2290 | goto out_unlock; | 2288 | goto out_put_task; |
2291 | } | 2289 | } |
2292 | for (i = 0, vma = mm->mmap, pos = 2; vma; | 2290 | for (i = 0, vma = mm->mmap, pos = 2; vma; |
2293 | vma = vma->vm_next) { | 2291 | vma = vma->vm_next) { |
@@ -2332,8 +2330,6 @@ proc_map_files_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
2332 | } | 2330 | } |
2333 | } | 2331 | } |
2334 | 2332 | ||
2335 | out_unlock: | ||
2336 | unlock_trace(task); | ||
2337 | out_put_task: | 2333 | out_put_task: |
2338 | put_task_struct(task); | 2334 | put_task_struct(task); |
2339 | out: | 2335 | out: |