diff options
Diffstat (limited to 'kernel/ptrace.c')
| -rw-r--r-- | kernel/ptrace.c | 84 |
1 files changed, 23 insertions, 61 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 921c22ad16e4..4d50e06fd745 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
| @@ -28,7 +28,7 @@ | |||
| 28 | * | 28 | * |
| 29 | * Must be called with the tasklist lock write-held. | 29 | * Must be called with the tasklist lock write-held. |
| 30 | */ | 30 | */ |
| 31 | void __ptrace_link(task_t *child, task_t *new_parent) | 31 | void __ptrace_link(struct task_struct *child, struct task_struct *new_parent) |
| 32 | { | 32 | { |
| 33 | BUG_ON(!list_empty(&child->ptrace_list)); | 33 | BUG_ON(!list_empty(&child->ptrace_list)); |
| 34 | if (child->parent == new_parent) | 34 | if (child->parent == new_parent) |
| @@ -46,7 +46,7 @@ void __ptrace_link(task_t *child, task_t *new_parent) | |||
| 46 | * TASK_TRACED, resume it now. | 46 | * TASK_TRACED, resume it now. |
| 47 | * Requires that irqs be disabled. | 47 | * Requires that irqs be disabled. |
| 48 | */ | 48 | */ |
| 49 | void ptrace_untrace(task_t *child) | 49 | void ptrace_untrace(struct task_struct *child) |
| 50 | { | 50 | { |
| 51 | spin_lock(&child->sighand->siglock); | 51 | spin_lock(&child->sighand->siglock); |
| 52 | if (child->state == TASK_TRACED) { | 52 | if (child->state == TASK_TRACED) { |
| @@ -65,7 +65,7 @@ void ptrace_untrace(task_t *child) | |||
| 65 | * | 65 | * |
| 66 | * Must be called with the tasklist lock write-held. | 66 | * Must be called with the tasklist lock write-held. |
| 67 | */ | 67 | */ |
| 68 | void __ptrace_unlink(task_t *child) | 68 | void __ptrace_unlink(struct task_struct *child) |
| 69 | { | 69 | { |
| 70 | BUG_ON(!child->ptrace); | 70 | BUG_ON(!child->ptrace); |
| 71 | 71 | ||
| @@ -120,8 +120,18 @@ int ptrace_check_attach(struct task_struct *child, int kill) | |||
| 120 | 120 | ||
| 121 | static int may_attach(struct task_struct *task) | 121 | static int may_attach(struct task_struct *task) |
| 122 | { | 122 | { |
| 123 | if (!task->mm) | 123 | /* May we inspect the given task? |
| 124 | return -EPERM; | 124 | * This check is used both for attaching with ptrace |
| 125 | * and for allowing access to sensitive information in /proc. | ||
| 126 | * | ||
| 127 | * ptrace_attach denies several cases that /proc allows | ||
| 128 | * because setting up the necessary parent/child relationship | ||
| 129 | * or halting the specified task is impossible. | ||
| 130 | */ | ||
| 131 | int dumpable = 0; | ||
| 132 | /* Don't let security modules deny introspection */ | ||
| 133 | if (task == current) | ||
| 134 | return 0; | ||
| 125 | if (((current->uid != task->euid) || | 135 | if (((current->uid != task->euid) || |
| 126 | (current->uid != task->suid) || | 136 | (current->uid != task->suid) || |
| 127 | (current->uid != task->uid) || | 137 | (current->uid != task->uid) || |
| @@ -130,7 +140,9 @@ static int may_attach(struct task_struct *task) | |||
| 130 | (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE)) | 140 | (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE)) |
| 131 | return -EPERM; | 141 | return -EPERM; |
| 132 | smp_rmb(); | 142 | smp_rmb(); |
| 133 | if (!task->mm->dumpable && !capable(CAP_SYS_PTRACE)) | 143 | if (task->mm) |
| 144 | dumpable = task->mm->dumpable; | ||
| 145 | if (!dumpable && !capable(CAP_SYS_PTRACE)) | ||
| 134 | return -EPERM; | 146 | return -EPERM; |
| 135 | 147 | ||
| 136 | return security_ptrace(current, task); | 148 | return security_ptrace(current, task); |
| @@ -176,6 +188,8 @@ repeat: | |||
| 176 | goto repeat; | 188 | goto repeat; |
| 177 | } | 189 | } |
| 178 | 190 | ||
| 191 | if (!task->mm) | ||
| 192 | goto bad; | ||
| 179 | /* the same process cannot be attached many times */ | 193 | /* the same process cannot be attached many times */ |
| 180 | if (task->ptrace & PT_PTRACED) | 194 | if (task->ptrace & PT_PTRACED) |
| 181 | goto bad; | 195 | goto bad; |
| @@ -200,7 +214,7 @@ out: | |||
| 200 | return retval; | 214 | return retval; |
| 201 | } | 215 | } |
| 202 | 216 | ||
| 203 | void __ptrace_detach(struct task_struct *child, unsigned int data) | 217 | static inline void __ptrace_detach(struct task_struct *child, unsigned int data) |
| 204 | { | 218 | { |
| 205 | child->exit_code = data; | 219 | child->exit_code = data; |
| 206 | /* .. re-parent .. */ | 220 | /* .. re-parent .. */ |
| @@ -219,6 +233,7 @@ int ptrace_detach(struct task_struct *child, unsigned int data) | |||
| 219 | ptrace_disable(child); | 233 | ptrace_disable(child); |
| 220 | 234 | ||
| 221 | write_lock_irq(&tasklist_lock); | 235 | write_lock_irq(&tasklist_lock); |
| 236 | /* protect against de_thread()->release_task() */ | ||
| 222 | if (child->ptrace) | 237 | if (child->ptrace) |
| 223 | __ptrace_detach(child, data); | 238 | __ptrace_detach(child, data); |
| 224 | write_unlock_irq(&tasklist_lock); | 239 | write_unlock_irq(&tasklist_lock); |
| @@ -226,60 +241,6 @@ int ptrace_detach(struct task_struct *child, unsigned int data) | |||
| 226 | return 0; | 241 | return 0; |
| 227 | } | 242 | } |
| 228 | 243 | ||
| 229 | /* | ||
| 230 | * Access another process' address space. | ||
| 231 | * Source/target buffer must be kernel space, | ||
| 232 | * Do not walk the page table directly, use get_user_pages | ||
| 233 | */ | ||
| 234 | |||
| 235 | int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write) | ||
| 236 | { | ||
| 237 | struct mm_struct *mm; | ||
| 238 | struct vm_area_struct *vma; | ||
| 239 | struct page *page; | ||
| 240 | void *old_buf = buf; | ||
| 241 | |||
| 242 | mm = get_task_mm(tsk); | ||
| 243 | if (!mm) | ||
| 244 | return 0; | ||
| 245 | |||
| 246 | down_read(&mm->mmap_sem); | ||
| 247 | /* ignore errors, just check how much was sucessfully transfered */ | ||
| 248 | while (len) { | ||
| 249 | int bytes, ret, offset; | ||
| 250 | void *maddr; | ||
| 251 | |||
| 252 | ret = get_user_pages(tsk, mm, addr, 1, | ||
| 253 | write, 1, &page, &vma); | ||
| 254 | if (ret <= 0) | ||
| 255 | break; | ||
| 256 | |||
| 257 | bytes = len; | ||
| 258 | offset = addr & (PAGE_SIZE-1); | ||
| 259 | if (bytes > PAGE_SIZE-offset) | ||
| 260 | bytes = PAGE_SIZE-offset; | ||
| 261 | |||
| 262 | maddr = kmap(page); | ||
| 263 | if (write) { | ||
| 264 | copy_to_user_page(vma, page, addr, | ||
| 265 | maddr + offset, buf, bytes); | ||
| 266 | set_page_dirty_lock(page); | ||
| 267 | } else { | ||
| 268 | copy_from_user_page(vma, page, addr, | ||
| 269 | buf, maddr + offset, bytes); | ||
| 270 | } | ||
| 271 | kunmap(page); | ||
| 272 | page_cache_release(page); | ||
| 273 | len -= bytes; | ||
| 274 | buf += bytes; | ||
| 275 | addr += bytes; | ||
| 276 | } | ||
| 277 | up_read(&mm->mmap_sem); | ||
| 278 | mmput(mm); | ||
| 279 | |||
| 280 | return buf - old_buf; | ||
| 281 | } | ||
| 282 | |||
| 283 | int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len) | 244 | int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len) |
| 284 | { | 245 | { |
| 285 | int copied = 0; | 246 | int copied = 0; |
| @@ -479,6 +440,7 @@ struct task_struct *ptrace_get_task_struct(pid_t pid) | |||
| 479 | child = find_task_by_pid(pid); | 440 | child = find_task_by_pid(pid); |
| 480 | if (child) | 441 | if (child) |
| 481 | get_task_struct(child); | 442 | get_task_struct(child); |
| 443 | |||
| 482 | read_unlock(&tasklist_lock); | 444 | read_unlock(&tasklist_lock); |
| 483 | if (!child) | 445 | if (!child) |
| 484 | return ERR_PTR(-ESRCH); | 446 | return ERR_PTR(-ESRCH); |
