aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
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 /kernel
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>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/ptrace.c42
1 files changed, 36 insertions, 6 deletions
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;