summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2017-05-22 16:40:12 -0400
committerEric W. Biederman <ebiederm@xmission.com>2017-05-23 08:40:44 -0400
commitc70d9d809fdeecedb96972457ee45c49a232d97f (patch)
treed814992a6f46fdc1b9faac491aaa30744cb2ed57
parent3fd37226216620c1a468afa999739d5016fbc349 (diff)
ptrace: Properly initialize ptracer_cred on fork
When I introduced ptracer_cred I failed to consider the weirdness of fork where the task_struct copies the old value by default. This winds up leaving ptracer_cred set even when a process forks and the child process does not wind up being ptraced. Because ptracer_cred is not set on non-ptraced processes whose parents were ptraced this has broken the ability of the enlightenment window manager to start setuid children. Fix this by properly initializing ptracer_cred in ptrace_init_task This must be done with a little bit of care to preserve the current value of ptracer_cred when ptrace carries through fork. Re-reading the ptracer_cred from the ptracing process at this point is inconsistent with how PT_PTRACE_CAP has been maintained all of these years. Tested-by: Takashi Iwai <tiwai@suse.de> Fixes: 64b875f7ac8a ("ptrace: Capture the ptracer's creds not PT_PTRACE_CAP") Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
-rw-r--r--include/linux/ptrace.h7
-rw-r--r--kernel/ptrace.c20
2 files changed, 18 insertions, 9 deletions
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index 422bc2e4cb6a..ef3eb8bbfee4 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -54,7 +54,8 @@ extern int ptrace_request(struct task_struct *child, long request,
54 unsigned long addr, unsigned long data); 54 unsigned long addr, unsigned long data);
55extern void ptrace_notify(int exit_code); 55extern void ptrace_notify(int exit_code);
56extern void __ptrace_link(struct task_struct *child, 56extern void __ptrace_link(struct task_struct *child,
57 struct task_struct *new_parent); 57 struct task_struct *new_parent,
58 const struct cred *ptracer_cred);
58extern void __ptrace_unlink(struct task_struct *child); 59extern void __ptrace_unlink(struct task_struct *child);
59extern void exit_ptrace(struct task_struct *tracer, struct list_head *dead); 60extern void exit_ptrace(struct task_struct *tracer, struct list_head *dead);
60#define PTRACE_MODE_READ 0x01 61#define PTRACE_MODE_READ 0x01
@@ -206,7 +207,7 @@ static inline void ptrace_init_task(struct task_struct *child, bool ptrace)
206 207
207 if (unlikely(ptrace) && current->ptrace) { 208 if (unlikely(ptrace) && current->ptrace) {
208 child->ptrace = current->ptrace; 209 child->ptrace = current->ptrace;
209 __ptrace_link(child, current->parent); 210 __ptrace_link(child, current->parent, current->ptracer_cred);
210 211
211 if (child->ptrace & PT_SEIZED) 212 if (child->ptrace & PT_SEIZED)
212 task_set_jobctl_pending(child, JOBCTL_TRAP_STOP); 213 task_set_jobctl_pending(child, JOBCTL_TRAP_STOP);
@@ -215,6 +216,8 @@ static inline void ptrace_init_task(struct task_struct *child, bool ptrace)
215 216
216 set_tsk_thread_flag(child, TIF_SIGPENDING); 217 set_tsk_thread_flag(child, TIF_SIGPENDING);
217 } 218 }
219 else
220 child->ptracer_cred = NULL;
218} 221}
219 222
220/** 223/**
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 266ddcc1d8bb..60f356d91060 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -60,19 +60,25 @@ int ptrace_access_vm(struct task_struct *tsk, unsigned long addr,
60} 60}
61 61
62 62
63void __ptrace_link(struct task_struct *child, struct task_struct *new_parent,
64 const struct cred *ptracer_cred)
65{
66 BUG_ON(!list_empty(&child->ptrace_entry));
67 list_add(&child->ptrace_entry, &new_parent->ptraced);
68 child->parent = new_parent;
69 child->ptracer_cred = get_cred(ptracer_cred);
70}
71
63/* 72/*
64 * ptrace a task: make the debugger its new parent and 73 * ptrace a task: make the debugger its new parent and
65 * move it to the ptrace list. 74 * move it to the ptrace list.
66 * 75 *
67 * Must be called with the tasklist lock write-held. 76 * Must be called with the tasklist lock write-held.
68 */ 77 */
69void __ptrace_link(struct task_struct *child, struct task_struct *new_parent) 78static void ptrace_link(struct task_struct *child, struct task_struct *new_parent)
70{ 79{
71 BUG_ON(!list_empty(&child->ptrace_entry));
72 list_add(&child->ptrace_entry, &new_parent->ptraced);
73 child->parent = new_parent;
74 rcu_read_lock(); 80 rcu_read_lock();
75 child->ptracer_cred = get_cred(__task_cred(new_parent)); 81 __ptrace_link(child, new_parent, __task_cred(new_parent));
76 rcu_read_unlock(); 82 rcu_read_unlock();
77} 83}
78 84
@@ -386,7 +392,7 @@ static int ptrace_attach(struct task_struct *task, long request,
386 flags |= PT_SEIZED; 392 flags |= PT_SEIZED;
387 task->ptrace = flags; 393 task->ptrace = flags;
388 394
389 __ptrace_link(task, current); 395 ptrace_link(task, current);
390 396
391 /* SEIZE doesn't trap tracee on attach */ 397 /* SEIZE doesn't trap tracee on attach */
392 if (!seize) 398 if (!seize)
@@ -459,7 +465,7 @@ static int ptrace_traceme(void)
459 */ 465 */
460 if (!ret && !(current->real_parent->flags & PF_EXITING)) { 466 if (!ret && !(current->real_parent->flags & PF_EXITING)) {
461 current->ptrace = PT_PTRACED; 467 current->ptrace = PT_PTRACED;
462 __ptrace_link(current, current->real_parent); 468 ptrace_link(current, current->real_parent);
463 } 469 }
464 } 470 }
465 write_unlock_irq(&tasklist_lock); 471 write_unlock_irq(&tasklist_lock);