Skip to content

Commit 84d77d3

Browse files
committed
ptrace: Don't allow accessing an undumpable mm
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 Cc: [email protected] Reported-by: Andy Lutomirski <[email protected]> Signed-off-by: "Eric W. Biederman" <[email protected]>
1 parent 64b875f commit 84d77d3

File tree

11 files changed

+52
-17
lines changed

11 files changed

+52
-17
lines changed

arch/alpha/kernel/ptrace.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ long arch_ptrace(struct task_struct *child, long request,
283283
/* When I and D space are separate, these will need to be fixed. */
284284
case PTRACE_PEEKTEXT: /* read word at location addr. */
285285
case PTRACE_PEEKDATA:
286-
copied = access_process_vm(child, addr, &tmp, sizeof(tmp),
286+
copied = ptrace_access_vm(child, addr, &tmp, sizeof(tmp),
287287
FOLL_FORCE);
288288
ret = -EIO;
289289
if (copied != sizeof(tmp))

arch/blackfin/kernel/ptrace.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ long arch_ptrace(struct task_struct *child, long request,
270270
switch (bfin_mem_access_type(addr, to_copy)) {
271271
case BFIN_MEM_ACCESS_CORE:
272272
case BFIN_MEM_ACCESS_CORE_ONLY:
273-
copied = access_process_vm(child, addr, &tmp,
273+
copied = ptrace_access_vm(child, addr, &tmp,
274274
to_copy, FOLL_FORCE);
275275
if (copied)
276276
break;
@@ -323,7 +323,7 @@ long arch_ptrace(struct task_struct *child, long request,
323323
switch (bfin_mem_access_type(addr, to_copy)) {
324324
case BFIN_MEM_ACCESS_CORE:
325325
case BFIN_MEM_ACCESS_CORE_ONLY:
326-
copied = access_process_vm(child, addr, &data,
326+
copied = ptrace_access_vm(child, addr, &data,
327327
to_copy,
328328
FOLL_FORCE | FOLL_WRITE);
329329
break;

arch/cris/arch-v32/kernel/ptrace.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ long arch_ptrace(struct task_struct *child, long request,
147147
/* The trampoline page is globally mapped, no page table to traverse.*/
148148
tmp = *(unsigned long*)addr;
149149
} 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);
151151

152152
if (copied != sizeof(tmp))
153153
break;

arch/ia64/kernel/ptrace.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1159,7 +1159,7 @@ arch_ptrace (struct task_struct *child, long request,
11591159
case PTRACE_PEEKTEXT:
11601160
case PTRACE_PEEKDATA:
11611161
/* 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),
11631163
FOLL_FORCE)
11641164
!= sizeof(data))
11651165
return -EIO;

arch/mips/kernel/ptrace32.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
6969
if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0)
7070
break;
7171

72-
copied = access_process_vm(child, (u64)addrOthers, &tmp,
72+
copied = ptrace_access_vm(child, (u64)addrOthers, &tmp,
7373
sizeof(tmp), FOLL_FORCE);
7474
if (copied != sizeof(tmp))
7575
break;
@@ -178,7 +178,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
178178
if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0)
179179
break;
180180
ret = 0;
181-
if (access_process_vm(child, (u64)addrOthers, &data,
181+
if (ptrace_access_vm(child, (u64)addrOthers, &data,
182182
sizeof(data),
183183
FOLL_FORCE | FOLL_WRITE) == sizeof(data))
184184
break;

arch/powerpc/kernel/ptrace32.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
7373
if (get_user(addrOthers, (u32 __user * __user *)addr) != 0)
7474
break;
7575

76-
copied = access_process_vm(child, (u64)addrOthers, &tmp,
76+
copied = ptrace_access_vm(child, (u64)addrOthers, &tmp,
7777
sizeof(tmp), FOLL_FORCE);
7878
if (copied != sizeof(tmp))
7979
break;
@@ -178,7 +178,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
178178
if (get_user(addrOthers, (u32 __user * __user *)addr) != 0)
179179
break;
180180
ret = 0;
181-
if (access_process_vm(child, (u64)addrOthers, &tmp,
181+
if (ptrace_access_vm(child, (u64)addrOthers, &tmp,
182182
sizeof(tmp),
183183
FOLL_FORCE | FOLL_WRITE) == sizeof(tmp))
184184
break;

include/linux/mm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1270,6 +1270,8 @@ extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *
12701270
unsigned int gup_flags);
12711271
extern int access_remote_vm(struct mm_struct *mm, unsigned long addr,
12721272
void *buf, int len, unsigned int gup_flags);
1273+
extern int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
1274+
unsigned long addr, void *buf, int len, unsigned int gup_flags);
12731275

12741276
long get_user_pages_remote(struct task_struct *tsk, struct mm_struct *mm,
12751277
unsigned long start, unsigned long nr_pages,

include/linux/ptrace.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
#include <linux/pid_namespace.h> /* For task_active_pid_ns. */
99
#include <uapi/linux/ptrace.h>
1010

11+
extern int ptrace_access_vm(struct task_struct *tsk, unsigned long addr,
12+
void *buf, int len, unsigned int gup_flags);
13+
1114
/*
1215
* Ptrace flags
1316
*

kernel/ptrace.c

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,35 @@
2727
#include <linux/cn_proc.h>
2828
#include <linux/compat.h>
2929

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+
*/
35+
int 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+
3059

3160
/*
3261
* 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
535564
int this_len, retval;
536565

537566
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+
539569
if (!retval) {
540570
if (copied)
541571
break;
@@ -562,7 +592,7 @@ int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long ds
562592
this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
563593
if (copy_from_user(buf, src, this_len))
564594
return -EFAULT;
565-
retval = access_process_vm(tsk, dst, buf, this_len,
595+
retval = ptrace_access_vm(tsk, dst, buf, this_len,
566596
FOLL_FORCE | FOLL_WRITE);
567597
if (!retval) {
568598
if (copied)
@@ -1126,7 +1156,7 @@ int generic_ptrace_peekdata(struct task_struct *tsk, unsigned long addr,
11261156
unsigned long tmp;
11271157
int copied;
11281158

1129-
copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), FOLL_FORCE);
1159+
copied = ptrace_access_vm(tsk, addr, &tmp, sizeof(tmp), FOLL_FORCE);
11301160
if (copied != sizeof(tmp))
11311161
return -EIO;
11321162
return put_user(tmp, (unsigned long __user *)data);
@@ -1137,7 +1167,7 @@ int generic_ptrace_pokedata(struct task_struct *tsk, unsigned long addr,
11371167
{
11381168
int copied;
11391169

1140-
copied = access_process_vm(tsk, addr, &data, sizeof(data),
1170+
copied = ptrace_access_vm(tsk, addr, &data, sizeof(data),
11411171
FOLL_FORCE | FOLL_WRITE);
11421172
return (copied == sizeof(data)) ? 0 : -EIO;
11431173
}
@@ -1155,7 +1185,7 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request,
11551185
switch (request) {
11561186
case PTRACE_PEEKTEXT:
11571187
case PTRACE_PEEKDATA:
1158-
ret = access_process_vm(child, addr, &word, sizeof(word),
1188+
ret = ptrace_access_vm(child, addr, &word, sizeof(word),
11591189
FOLL_FORCE);
11601190
if (ret != sizeof(word))
11611191
ret = -EIO;
@@ -1165,7 +1195,7 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request,
11651195

11661196
case PTRACE_POKETEXT:
11671197
case PTRACE_POKEDATA:
1168-
ret = access_process_vm(child, addr, &data, sizeof(data),
1198+
ret = ptrace_access_vm(child, addr, &data, sizeof(data),
11691199
FOLL_FORCE | FOLL_WRITE);
11701200
ret = (ret != sizeof(data) ? -EIO : 0);
11711201
break;

mm/memory.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3868,7 +3868,7 @@ EXPORT_SYMBOL_GPL(generic_access_phys);
38683868
* Access another process' address space as given in mm. If non-NULL, use the
38693869
* given task for page fault accounting.
38703870
*/
3871-
static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
3871+
int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
38723872
unsigned long addr, void *buf, int len, unsigned int gup_flags)
38733873
{
38743874
struct vm_area_struct *vma;

mm/nommu.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1808,7 +1808,7 @@ void filemap_map_pages(struct fault_env *fe,
18081808
}
18091809
EXPORT_SYMBOL(filemap_map_pages);
18101810

1811-
static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
1811+
int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
18121812
unsigned long addr, void *buf, int len, unsigned int gup_flags)
18131813
{
18141814
struct vm_area_struct *vma;

0 commit comments

Comments
 (0)