diff options
Diffstat (limited to 'kernel/ptrace.c')
-rw-r--r-- | kernel/ptrace.c | 50 |
1 files changed, 35 insertions, 15 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 1708b1e2972d..dc7ab65f3b36 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/syscalls.h> | 22 | #include <linux/syscalls.h> |
23 | #include <linux/uaccess.h> | 23 | #include <linux/uaccess.h> |
24 | #include <linux/regset.h> | 24 | #include <linux/regset.h> |
25 | #include <linux/hw_breakpoint.h> | ||
25 | 26 | ||
26 | 27 | ||
27 | /* | 28 | /* |
@@ -134,21 +135,24 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode) | |||
134 | return 0; | 135 | return 0; |
135 | rcu_read_lock(); | 136 | rcu_read_lock(); |
136 | tcred = __task_cred(task); | 137 | tcred = __task_cred(task); |
137 | if ((cred->uid != tcred->euid || | 138 | if (cred->user->user_ns == tcred->user->user_ns && |
138 | cred->uid != tcred->suid || | 139 | (cred->uid == tcred->euid && |
139 | cred->uid != tcred->uid || | 140 | cred->uid == tcred->suid && |
140 | cred->gid != tcred->egid || | 141 | cred->uid == tcred->uid && |
141 | cred->gid != tcred->sgid || | 142 | cred->gid == tcred->egid && |
142 | cred->gid != tcred->gid) && | 143 | cred->gid == tcred->sgid && |
143 | !capable(CAP_SYS_PTRACE)) { | 144 | cred->gid == tcred->gid)) |
144 | rcu_read_unlock(); | 145 | goto ok; |
145 | return -EPERM; | 146 | if (ns_capable(tcred->user->user_ns, CAP_SYS_PTRACE)) |
146 | } | 147 | goto ok; |
148 | rcu_read_unlock(); | ||
149 | return -EPERM; | ||
150 | ok: | ||
147 | rcu_read_unlock(); | 151 | rcu_read_unlock(); |
148 | smp_rmb(); | 152 | smp_rmb(); |
149 | if (task->mm) | 153 | if (task->mm) |
150 | dumpable = get_dumpable(task->mm); | 154 | dumpable = get_dumpable(task->mm); |
151 | if (!dumpable && !capable(CAP_SYS_PTRACE)) | 155 | if (!dumpable && !task_ns_capable(task, CAP_SYS_PTRACE)) |
152 | return -EPERM; | 156 | return -EPERM; |
153 | 157 | ||
154 | return security_ptrace_access_check(task, mode); | 158 | return security_ptrace_access_check(task, mode); |
@@ -163,7 +167,7 @@ bool ptrace_may_access(struct task_struct *task, unsigned int mode) | |||
163 | return !err; | 167 | return !err; |
164 | } | 168 | } |
165 | 169 | ||
166 | int ptrace_attach(struct task_struct *task) | 170 | static int ptrace_attach(struct task_struct *task) |
167 | { | 171 | { |
168 | int retval; | 172 | int retval; |
169 | 173 | ||
@@ -198,7 +202,7 @@ int ptrace_attach(struct task_struct *task) | |||
198 | goto unlock_tasklist; | 202 | goto unlock_tasklist; |
199 | 203 | ||
200 | task->ptrace = PT_PTRACED; | 204 | task->ptrace = PT_PTRACED; |
201 | if (capable(CAP_SYS_PTRACE)) | 205 | if (task_ns_capable(task, CAP_SYS_PTRACE)) |
202 | task->ptrace |= PT_PTRACE_CAP; | 206 | task->ptrace |= PT_PTRACE_CAP; |
203 | 207 | ||
204 | __ptrace_link(task, current); | 208 | __ptrace_link(task, current); |
@@ -219,7 +223,7 @@ out: | |||
219 | * Performs checks and sets PT_PTRACED. | 223 | * Performs checks and sets PT_PTRACED. |
220 | * Should be used by all ptrace implementations for PTRACE_TRACEME. | 224 | * Should be used by all ptrace implementations for PTRACE_TRACEME. |
221 | */ | 225 | */ |
222 | int ptrace_traceme(void) | 226 | static int ptrace_traceme(void) |
223 | { | 227 | { |
224 | int ret = -EPERM; | 228 | int ret = -EPERM; |
225 | 229 | ||
@@ -293,7 +297,7 @@ static bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p) | |||
293 | return false; | 297 | return false; |
294 | } | 298 | } |
295 | 299 | ||
296 | int ptrace_detach(struct task_struct *child, unsigned int data) | 300 | static int ptrace_detach(struct task_struct *child, unsigned int data) |
297 | { | 301 | { |
298 | bool dead = false; | 302 | bool dead = false; |
299 | 303 | ||
@@ -876,3 +880,19 @@ asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid, | |||
876 | return ret; | 880 | return ret; |
877 | } | 881 | } |
878 | #endif /* CONFIG_COMPAT */ | 882 | #endif /* CONFIG_COMPAT */ |
883 | |||
884 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | ||
885 | int ptrace_get_breakpoints(struct task_struct *tsk) | ||
886 | { | ||
887 | if (atomic_inc_not_zero(&tsk->ptrace_bp_refcnt)) | ||
888 | return 0; | ||
889 | |||
890 | return -1; | ||
891 | } | ||
892 | |||
893 | void ptrace_put_breakpoints(struct task_struct *tsk) | ||
894 | { | ||
895 | if (atomic_dec_and_test(&tsk->ptrace_bp_refcnt)) | ||
896 | flush_ptrace_hw_breakpoint(tsk); | ||
897 | } | ||
898 | #endif /* CONFIG_HAVE_HW_BREAKPOINT */ | ||