aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2016-11-22 13:06:50 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-01-06 04:40:13 -0500
commite71b4e061c9677cef1f1f38fd7236e198fab1287 (patch)
treeca6ebd0889260f299e091cee694d79fdfea23dad
parente747b4ae3b6bca205d82e86366e140cdcbfb7731 (diff)
ptrace: Don't allow accessing an undumpable mm
commit 84d77d3f06e7e8dea057d10e8ec77ad71f721be3 upstream. It is the reasonable expectation that if an executable file is not readable there will be no way for a user without special privileges to read the file. This is enforced in ptrace_attach but if ptrace is already attached before exec there is no enforcement for read-only executables. As the only way to read such an mm is through access_process_vm spin a variant called ptrace_access_vm that will fail if the target process is not being ptraced by the current process, or the current process did not have sufficient privileges when ptracing began to read the target processes mm. In the ptrace implementations replace access_process_vm by ptrace_access_vm. There remain several ptrace sites that still use access_process_vm as they are reading the target executables instructions (for kernel consumption) or register stacks. As such it does not appear necessary to add a permission check to those calls. This bug has always existed in Linux. Fixes: v1.0 Reported-by: Andy Lutomirski <luto@amacapital.net> Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--arch/alpha/kernel/ptrace.c2
-rw-r--r--arch/blackfin/kernel/ptrace.c4
-rw-r--r--arch/cris/arch-v32/kernel/ptrace.c2
-rw-r--r--arch/ia64/kernel/ptrace.c2
-rw-r--r--arch/mips/kernel/ptrace32.c4
-rw-r--r--arch/powerpc/kernel/ptrace32.c4
-rw-r--r--include/linux/mm.h2
-rw-r--r--include/linux/ptrace.h3
-rw-r--r--kernel/ptrace.c42
-rw-r--r--mm/memory.c2
-rw-r--r--mm/nommu.c2
11 files changed, 52 insertions, 17 deletions
diff --git a/arch/alpha/kernel/ptrace.c b/arch/alpha/kernel/ptrace.c
index 940dfb406591..04abdec7f496 100644
--- a/arch/alpha/kernel/ptrace.c
+++ b/arch/alpha/kernel/ptrace.c
@@ -283,7 +283,7 @@ long arch_ptrace(struct task_struct *child, long request,
283 /* When I and D space are separate, these will need to be fixed. */ 283 /* When I and D space are separate, these will need to be fixed. */
284 case PTRACE_PEEKTEXT: /* read word at location addr. */ 284 case PTRACE_PEEKTEXT: /* read word at location addr. */
285 case PTRACE_PEEKDATA: 285 case PTRACE_PEEKDATA:
286 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 286 copied = ptrace_access_vm(child, addr, &tmp, sizeof(tmp),
287 FOLL_FORCE); 287 FOLL_FORCE);
288 ret = -EIO; 288 ret = -EIO;
289 if (copied != sizeof(tmp)) 289 if (copied != sizeof(tmp))
diff --git a/arch/blackfin/kernel/ptrace.c b/arch/blackfin/kernel/ptrace.c
index 8d79286ee4e8..360d99645163 100644
--- a/arch/blackfin/kernel/ptrace.c
+++ b/arch/blackfin/kernel/ptrace.c
@@ -270,7 +270,7 @@ long arch_ptrace(struct task_struct *child, long request,
270 switch (bfin_mem_access_type(addr, to_copy)) { 270 switch (bfin_mem_access_type(addr, to_copy)) {
271 case BFIN_MEM_ACCESS_CORE: 271 case BFIN_MEM_ACCESS_CORE:
272 case BFIN_MEM_ACCESS_CORE_ONLY: 272 case BFIN_MEM_ACCESS_CORE_ONLY:
273 copied = access_process_vm(child, addr, &tmp, 273 copied = ptrace_access_vm(child, addr, &tmp,
274 to_copy, FOLL_FORCE); 274 to_copy, FOLL_FORCE);
275 if (copied) 275 if (copied)
276 break; 276 break;
@@ -323,7 +323,7 @@ long arch_ptrace(struct task_struct *child, long request,
323 switch (bfin_mem_access_type(addr, to_copy)) { 323 switch (bfin_mem_access_type(addr, to_copy)) {
324 case BFIN_MEM_ACCESS_CORE: 324 case BFIN_MEM_ACCESS_CORE:
325 case BFIN_MEM_ACCESS_CORE_ONLY: 325 case BFIN_MEM_ACCESS_CORE_ONLY:
326 copied = access_process_vm(child, addr, &data, 326 copied = ptrace_access_vm(child, addr, &data,
327 to_copy, 327 to_copy,
328 FOLL_FORCE | FOLL_WRITE); 328 FOLL_FORCE | FOLL_WRITE);
329 break; 329 break;
diff --git a/arch/cris/arch-v32/kernel/ptrace.c b/arch/cris/arch-v32/kernel/ptrace.c
index f0df654ac6fc..fe1f9cf7b391 100644
--- a/arch/cris/arch-v32/kernel/ptrace.c
+++ b/arch/cris/arch-v32/kernel/ptrace.c
@@ -147,7 +147,7 @@ long arch_ptrace(struct task_struct *child, long request,
147 /* The trampoline page is globally mapped, no page table to traverse.*/ 147 /* The trampoline page is globally mapped, no page table to traverse.*/
148 tmp = *(unsigned long*)addr; 148 tmp = *(unsigned long*)addr;
149 } else { 149 } else {
150 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), FOLL_FORCE); 150 copied = ptrace_access_vm(child, addr, &tmp, sizeof(tmp), FOLL_FORCE);
151 151
152 if (copied != sizeof(tmp)) 152 if (copied != sizeof(tmp))
153 break; 153 break;
diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c
index 31aa8c0f68e1..36f660da8124 100644
--- a/arch/ia64/kernel/ptrace.c
+++ b/arch/ia64/kernel/ptrace.c
@@ -1159,7 +1159,7 @@ arch_ptrace (struct task_struct *child, long request,
1159 case PTRACE_PEEKTEXT: 1159 case PTRACE_PEEKTEXT:
1160 case PTRACE_PEEKDATA: 1160 case PTRACE_PEEKDATA:
1161 /* read word at location addr */ 1161 /* read word at location addr */
1162 if (access_process_vm(child, addr, &data, sizeof(data), 1162 if (ptrace_access_vm(child, addr, &data, sizeof(data),
1163 FOLL_FORCE) 1163 FOLL_FORCE)
1164 != sizeof(data)) 1164 != sizeof(data))
1165 return -EIO; 1165 return -EIO;
diff --git a/arch/mips/kernel/ptrace32.c b/arch/mips/kernel/ptrace32.c
index 7e71a4e0281b..5fcbdcd7abd0 100644
--- a/arch/mips/kernel/ptrace32.c
+++ b/arch/mips/kernel/ptrace32.c
@@ -69,7 +69,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
69 if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0) 69 if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0)
70 break; 70 break;
71 71
72 copied = access_process_vm(child, (u64)addrOthers, &tmp, 72 copied = ptrace_access_vm(child, (u64)addrOthers, &tmp,
73 sizeof(tmp), FOLL_FORCE); 73 sizeof(tmp), FOLL_FORCE);
74 if (copied != sizeof(tmp)) 74 if (copied != sizeof(tmp))
75 break; 75 break;
@@ -178,7 +178,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
178 if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0) 178 if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0)
179 break; 179 break;
180 ret = 0; 180 ret = 0;
181 if (access_process_vm(child, (u64)addrOthers, &data, 181 if (ptrace_access_vm(child, (u64)addrOthers, &data,
182 sizeof(data), 182 sizeof(data),
183 FOLL_FORCE | FOLL_WRITE) == sizeof(data)) 183 FOLL_FORCE | FOLL_WRITE) == sizeof(data))
184 break; 184 break;
diff --git a/arch/powerpc/kernel/ptrace32.c b/arch/powerpc/kernel/ptrace32.c
index 010b7b310237..1e887f3a61a6 100644
--- a/arch/powerpc/kernel/ptrace32.c
+++ b/arch/powerpc/kernel/ptrace32.c
@@ -73,7 +73,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
73 if (get_user(addrOthers, (u32 __user * __user *)addr) != 0) 73 if (get_user(addrOthers, (u32 __user * __user *)addr) != 0)
74 break; 74 break;
75 75
76 copied = access_process_vm(child, (u64)addrOthers, &tmp, 76 copied = ptrace_access_vm(child, (u64)addrOthers, &tmp,
77 sizeof(tmp), FOLL_FORCE); 77 sizeof(tmp), FOLL_FORCE);
78 if (copied != sizeof(tmp)) 78 if (copied != sizeof(tmp))
79 break; 79 break;
@@ -178,7 +178,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
178 if (get_user(addrOthers, (u32 __user * __user *)addr) != 0) 178 if (get_user(addrOthers, (u32 __user * __user *)addr) != 0)
179 break; 179 break;
180 ret = 0; 180 ret = 0;
181 if (access_process_vm(child, (u64)addrOthers, &tmp, 181 if (ptrace_access_vm(child, (u64)addrOthers, &tmp,
182 sizeof(tmp), 182 sizeof(tmp),
183 FOLL_FORCE | FOLL_WRITE) == sizeof(tmp)) 183 FOLL_FORCE | FOLL_WRITE) == sizeof(tmp))
184 break; 184 break;
diff --git a/include/linux/mm.h b/include/linux/mm.h
index a92c8d73aeaf..0b5b2e4df14e 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1270,6 +1270,8 @@ extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *
1270 unsigned int gup_flags); 1270 unsigned int gup_flags);
1271extern int access_remote_vm(struct mm_struct *mm, unsigned long addr, 1271extern int access_remote_vm(struct mm_struct *mm, unsigned long addr,
1272 void *buf, int len, unsigned int gup_flags); 1272 void *buf, int len, unsigned int gup_flags);
1273extern int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
1274 unsigned long addr, void *buf, int len, unsigned int gup_flags);
1273 1275
1274long get_user_pages_remote(struct task_struct *tsk, struct mm_struct *mm, 1276long get_user_pages_remote(struct task_struct *tsk, struct mm_struct *mm,
1275 unsigned long start, unsigned long nr_pages, 1277 unsigned long start, unsigned long nr_pages,
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index e13bfdf7f314..e0e539321ab9 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -8,6 +8,9 @@
8#include <linux/pid_namespace.h> /* For task_active_pid_ns. */ 8#include <linux/pid_namespace.h> /* For task_active_pid_ns. */
9#include <uapi/linux/ptrace.h> 9#include <uapi/linux/ptrace.h>
10 10
11extern int ptrace_access_vm(struct task_struct *tsk, unsigned long addr,
12 void *buf, int len, unsigned int gup_flags);
13
11/* 14/*
12 * Ptrace flags 15 * Ptrace flags
13 * 16 *
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index e82c15cadd6d..49ba7c1ade9d 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -27,6 +27,35 @@
27#include <linux/cn_proc.h> 27#include <linux/cn_proc.h>
28#include <linux/compat.h> 28#include <linux/compat.h>
29 29
30/*
31 * Access another process' address space via ptrace.
32 * Source/target buffer must be kernel space,
33 * Do not walk the page table directly, use get_user_pages
34 */
35int ptrace_access_vm(struct task_struct *tsk, unsigned long addr,
36 void *buf, int len, unsigned int gup_flags)
37{
38 struct mm_struct *mm;
39 int ret;
40
41 mm = get_task_mm(tsk);
42 if (!mm)
43 return 0;
44
45 if (!tsk->ptrace ||
46 (current != tsk->parent) ||
47 ((get_dumpable(mm) != SUID_DUMP_USER) &&
48 !ptracer_capable(tsk, mm->user_ns))) {
49 mmput(mm);
50 return 0;
51 }
52
53 ret = __access_remote_vm(tsk, mm, addr, buf, len, gup_flags);
54 mmput(mm);
55
56 return ret;
57}
58
30 59
31/* 60/*
32 * ptrace a task: make the debugger its new parent and 61 * ptrace a task: make the debugger its new parent and
@@ -535,7 +564,8 @@ int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst
535 int this_len, retval; 564 int this_len, retval;
536 565
537 this_len = (len > sizeof(buf)) ? sizeof(buf) : len; 566 this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
538 retval = access_process_vm(tsk, src, buf, this_len, FOLL_FORCE); 567 retval = ptrace_access_vm(tsk, src, buf, this_len, FOLL_FORCE);
568
539 if (!retval) { 569 if (!retval) {
540 if (copied) 570 if (copied)
541 break; 571 break;
@@ -562,7 +592,7 @@ int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long ds
562 this_len = (len > sizeof(buf)) ? sizeof(buf) : len; 592 this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
563 if (copy_from_user(buf, src, this_len)) 593 if (copy_from_user(buf, src, this_len))
564 return -EFAULT; 594 return -EFAULT;
565 retval = access_process_vm(tsk, dst, buf, this_len, 595 retval = ptrace_access_vm(tsk, dst, buf, this_len,
566 FOLL_FORCE | FOLL_WRITE); 596 FOLL_FORCE | FOLL_WRITE);
567 if (!retval) { 597 if (!retval) {
568 if (copied) 598 if (copied)
@@ -1126,7 +1156,7 @@ int generic_ptrace_peekdata(struct task_struct *tsk, unsigned long addr,
1126 unsigned long tmp; 1156 unsigned long tmp;
1127 int copied; 1157 int copied;
1128 1158
1129 copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), FOLL_FORCE); 1159 copied = ptrace_access_vm(tsk, addr, &tmp, sizeof(tmp), FOLL_FORCE);
1130 if (copied != sizeof(tmp)) 1160 if (copied != sizeof(tmp))
1131 return -EIO; 1161 return -EIO;
1132 return put_user(tmp, (unsigned long __user *)data); 1162 return put_user(tmp, (unsigned long __user *)data);
@@ -1137,7 +1167,7 @@ int generic_ptrace_pokedata(struct task_struct *tsk, unsigned long addr,
1137{ 1167{
1138 int copied; 1168 int copied;
1139 1169
1140 copied = access_process_vm(tsk, addr, &data, sizeof(data), 1170 copied = ptrace_access_vm(tsk, addr, &data, sizeof(data),
1141 FOLL_FORCE | FOLL_WRITE); 1171 FOLL_FORCE | FOLL_WRITE);
1142 return (copied == sizeof(data)) ? 0 : -EIO; 1172 return (copied == sizeof(data)) ? 0 : -EIO;
1143} 1173}
@@ -1155,7 +1185,7 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request,
1155 switch (request) { 1185 switch (request) {
1156 case PTRACE_PEEKTEXT: 1186 case PTRACE_PEEKTEXT:
1157 case PTRACE_PEEKDATA: 1187 case PTRACE_PEEKDATA:
1158 ret = access_process_vm(child, addr, &word, sizeof(word), 1188 ret = ptrace_access_vm(child, addr, &word, sizeof(word),
1159 FOLL_FORCE); 1189 FOLL_FORCE);
1160 if (ret != sizeof(word)) 1190 if (ret != sizeof(word))
1161 ret = -EIO; 1191 ret = -EIO;
@@ -1165,7 +1195,7 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request,
1165 1195
1166 case PTRACE_POKETEXT: 1196 case PTRACE_POKETEXT:
1167 case PTRACE_POKEDATA: 1197 case PTRACE_POKEDATA:
1168 ret = access_process_vm(child, addr, &data, sizeof(data), 1198 ret = ptrace_access_vm(child, addr, &data, sizeof(data),
1169 FOLL_FORCE | FOLL_WRITE); 1199 FOLL_FORCE | FOLL_WRITE);
1170 ret = (ret != sizeof(data) ? -EIO : 0); 1200 ret = (ret != sizeof(data) ? -EIO : 0);
1171 break; 1201 break;
diff --git a/mm/memory.c b/mm/memory.c
index e18c57bdc75c..cbb1e5e5f791 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -3868,7 +3868,7 @@ EXPORT_SYMBOL_GPL(generic_access_phys);
3868 * Access another process' address space as given in mm. If non-NULL, use the 3868 * Access another process' address space as given in mm. If non-NULL, use the
3869 * given task for page fault accounting. 3869 * given task for page fault accounting.
3870 */ 3870 */
3871static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm, 3871int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
3872 unsigned long addr, void *buf, int len, unsigned int gup_flags) 3872 unsigned long addr, void *buf, int len, unsigned int gup_flags)
3873{ 3873{
3874 struct vm_area_struct *vma; 3874 struct vm_area_struct *vma;
diff --git a/mm/nommu.c b/mm/nommu.c
index 8b8faaf2a9e9..44265e00b701 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -1808,7 +1808,7 @@ void filemap_map_pages(struct fault_env *fe,
1808} 1808}
1809EXPORT_SYMBOL(filemap_map_pages); 1809EXPORT_SYMBOL(filemap_map_pages);
1810 1810
1811static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm, 1811int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
1812 unsigned long addr, void *buf, int len, unsigned int gup_flags) 1812 unsigned long addr, void *buf, int len, unsigned int gup_flags)
1813{ 1813{
1814 struct vm_area_struct *vma; 1814 struct vm_area_struct *vma;