diff options
Diffstat (limited to 'kernel/ptrace.c')
-rw-r--r-- | kernel/ptrace.c | 66 |
1 files changed, 25 insertions, 41 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 00ab2ca5ed11..ee8d49b9c309 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
@@ -231,26 +231,22 @@ bool ptrace_may_access(struct task_struct *task, unsigned int mode) | |||
231 | } | 231 | } |
232 | 232 | ||
233 | static int ptrace_attach(struct task_struct *task, long request, | 233 | static int ptrace_attach(struct task_struct *task, long request, |
234 | unsigned long addr, | ||
234 | unsigned long flags) | 235 | unsigned long flags) |
235 | { | 236 | { |
236 | bool seize = (request == PTRACE_SEIZE); | 237 | bool seize = (request == PTRACE_SEIZE); |
237 | int retval; | 238 | int retval; |
238 | 239 | ||
239 | /* | ||
240 | * SEIZE will enable new ptrace behaviors which will be implemented | ||
241 | * gradually. SEIZE_DEVEL is used to prevent applications | ||
242 | * expecting full SEIZE behaviors trapping on kernel commits which | ||
243 | * are still in the process of implementing them. | ||
244 | * | ||
245 | * Only test programs for new ptrace behaviors being implemented | ||
246 | * should set SEIZE_DEVEL. If unset, SEIZE will fail with -EIO. | ||
247 | * | ||
248 | * Once SEIZE behaviors are completely implemented, this flag and | ||
249 | * the following test will be removed. | ||
250 | */ | ||
251 | retval = -EIO; | 240 | retval = -EIO; |
252 | if (seize && !(flags & PTRACE_SEIZE_DEVEL)) | 241 | if (seize) { |
253 | goto out; | 242 | if (addr != 0) |
243 | goto out; | ||
244 | if (flags & ~(unsigned long)PTRACE_O_MASK) | ||
245 | goto out; | ||
246 | flags = PT_PTRACED | PT_SEIZED | (flags << PT_OPT_FLAG_SHIFT); | ||
247 | } else { | ||
248 | flags = PT_PTRACED; | ||
249 | } | ||
254 | 250 | ||
255 | audit_ptrace(task); | 251 | audit_ptrace(task); |
256 | 252 | ||
@@ -262,7 +258,7 @@ static int ptrace_attach(struct task_struct *task, long request, | |||
262 | 258 | ||
263 | /* | 259 | /* |
264 | * Protect exec's credential calculations against our interference; | 260 | * Protect exec's credential calculations against our interference; |
265 | * interference; SUID, SGID and LSM creds get determined differently | 261 | * SUID, SGID and LSM creds get determined differently |
266 | * under ptrace. | 262 | * under ptrace. |
267 | */ | 263 | */ |
268 | retval = -ERESTARTNOINTR; | 264 | retval = -ERESTARTNOINTR; |
@@ -282,11 +278,11 @@ static int ptrace_attach(struct task_struct *task, long request, | |||
282 | if (task->ptrace) | 278 | if (task->ptrace) |
283 | goto unlock_tasklist; | 279 | goto unlock_tasklist; |
284 | 280 | ||
285 | task->ptrace = PT_PTRACED; | ||
286 | if (seize) | 281 | if (seize) |
287 | task->ptrace |= PT_SEIZED; | 282 | flags |= PT_SEIZED; |
288 | if (ns_capable(task_user_ns(task), CAP_SYS_PTRACE)) | 283 | if (ns_capable(task_user_ns(task), CAP_SYS_PTRACE)) |
289 | task->ptrace |= PT_PTRACE_CAP; | 284 | flags |= PT_PTRACE_CAP; |
285 | task->ptrace = flags; | ||
290 | 286 | ||
291 | __ptrace_link(task, current); | 287 | __ptrace_link(task, current); |
292 | 288 | ||
@@ -528,30 +524,18 @@ int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long ds | |||
528 | 524 | ||
529 | static int ptrace_setoptions(struct task_struct *child, unsigned long data) | 525 | static int ptrace_setoptions(struct task_struct *child, unsigned long data) |
530 | { | 526 | { |
531 | child->ptrace &= ~PT_TRACE_MASK; | 527 | unsigned flags; |
532 | 528 | ||
533 | if (data & PTRACE_O_TRACESYSGOOD) | 529 | if (data & ~(unsigned long)PTRACE_O_MASK) |
534 | child->ptrace |= PT_TRACESYSGOOD; | 530 | return -EINVAL; |
535 | |||
536 | if (data & PTRACE_O_TRACEFORK) | ||
537 | child->ptrace |= PT_TRACE_FORK; | ||
538 | |||
539 | if (data & PTRACE_O_TRACEVFORK) | ||
540 | child->ptrace |= PT_TRACE_VFORK; | ||
541 | |||
542 | if (data & PTRACE_O_TRACECLONE) | ||
543 | child->ptrace |= PT_TRACE_CLONE; | ||
544 | |||
545 | if (data & PTRACE_O_TRACEEXEC) | ||
546 | child->ptrace |= PT_TRACE_EXEC; | ||
547 | |||
548 | if (data & PTRACE_O_TRACEVFORKDONE) | ||
549 | child->ptrace |= PT_TRACE_VFORK_DONE; | ||
550 | 531 | ||
551 | if (data & PTRACE_O_TRACEEXIT) | 532 | /* Avoid intermediate state when all opts are cleared */ |
552 | child->ptrace |= PT_TRACE_EXIT; | 533 | flags = child->ptrace; |
534 | flags &= ~(PTRACE_O_MASK << PT_OPT_FLAG_SHIFT); | ||
535 | flags |= (data << PT_OPT_FLAG_SHIFT); | ||
536 | child->ptrace = flags; | ||
553 | 537 | ||
554 | return (data & ~PTRACE_O_MASK) ? -EINVAL : 0; | 538 | return 0; |
555 | } | 539 | } |
556 | 540 | ||
557 | static int ptrace_getsiginfo(struct task_struct *child, siginfo_t *info) | 541 | static int ptrace_getsiginfo(struct task_struct *child, siginfo_t *info) |
@@ -891,7 +875,7 @@ SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr, | |||
891 | } | 875 | } |
892 | 876 | ||
893 | if (request == PTRACE_ATTACH || request == PTRACE_SEIZE) { | 877 | if (request == PTRACE_ATTACH || request == PTRACE_SEIZE) { |
894 | ret = ptrace_attach(child, request, data); | 878 | ret = ptrace_attach(child, request, addr, data); |
895 | /* | 879 | /* |
896 | * Some architectures need to do book-keeping after | 880 | * Some architectures need to do book-keeping after |
897 | * a ptrace attach. | 881 | * a ptrace attach. |
@@ -1034,7 +1018,7 @@ asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid, | |||
1034 | } | 1018 | } |
1035 | 1019 | ||
1036 | if (request == PTRACE_ATTACH || request == PTRACE_SEIZE) { | 1020 | if (request == PTRACE_ATTACH || request == PTRACE_SEIZE) { |
1037 | ret = ptrace_attach(child, request, data); | 1021 | ret = ptrace_attach(child, request, addr, data); |
1038 | /* | 1022 | /* |
1039 | * Some architectures need to do book-keeping after | 1023 | * Some architectures need to do book-keeping after |
1040 | * a ptrace attach. | 1024 | * a ptrace attach. |