linux/arch/sh/kernel/ptrace_32.c
<<
>>
Prefs
   1/*
   2 * SuperH process tracing
   3 *
   4 * Copyright (C) 1999, 2000  Kaz Kojima & Niibe Yutaka
   5 * Copyright (C) 2002 - 2009  Paul Mundt
   6 *
   7 * Audit support by Yuichi Nakamura <ynakam@hitachisoft.jp>
   8 *
   9 * This file is subject to the terms and conditions of the GNU General Public
  10 * License.  See the file "COPYING" in the main directory of this archive
  11 * for more details.
  12 */
  13#include <linux/kernel.h>
  14#include <linux/sched.h>
  15#include <linux/mm.h>
  16#include <linux/smp.h>
  17#include <linux/errno.h>
  18#include <linux/ptrace.h>
  19#include <linux/user.h>
  20#include <linux/security.h>
  21#include <linux/signal.h>
  22#include <linux/io.h>
  23#include <linux/audit.h>
  24#include <linux/seccomp.h>
  25#include <linux/tracehook.h>
  26#include <linux/elf.h>
  27#include <linux/regset.h>
  28#include <linux/hw_breakpoint.h>
  29#include <asm/uaccess.h>
  30#include <asm/pgtable.h>
  31#include <asm/processor.h>
  32#include <asm/mmu_context.h>
  33#include <asm/syscalls.h>
  34#include <asm/fpu.h>
  35
  36#define CREATE_TRACE_POINTS
  37#include <trace/events/syscalls.h>
  38
  39/*
  40 * This routine will get a word off of the process kernel stack.
  41 */
  42static inline int get_stack_long(struct task_struct *task, int offset)
  43{
  44        unsigned char *stack;
  45
  46        stack = (unsigned char *)task_pt_regs(task);
  47        stack += offset;
  48        return (*((int *)stack));
  49}
  50
  51/*
  52 * This routine will put a word on the process kernel stack.
  53 */
  54static inline int put_stack_long(struct task_struct *task, int offset,
  55                                 unsigned long data)
  56{
  57        unsigned char *stack;
  58
  59        stack = (unsigned char *)task_pt_regs(task);
  60        stack += offset;
  61        *(unsigned long *) stack = data;
  62        return 0;
  63}
  64
  65void ptrace_triggered(struct perf_event *bp,
  66                      struct perf_sample_data *data, struct pt_regs *regs)
  67{
  68        struct perf_event_attr attr;
  69
  70        /*
  71         * Disable the breakpoint request here since ptrace has defined a
  72         * one-shot behaviour for breakpoint exceptions.
  73         */
  74        attr = bp->attr;
  75        attr.disabled = true;
  76        modify_user_hw_breakpoint(bp, &attr);
  77}
  78
  79static int set_single_step(struct task_struct *tsk, unsigned long addr)
  80{
  81        struct thread_struct *thread = &tsk->thread;
  82        struct perf_event *bp;
  83        struct perf_event_attr attr;
  84
  85        bp = thread->ptrace_bps[0];
  86        if (!bp) {
  87                ptrace_breakpoint_init(&attr);
  88
  89                attr.bp_addr = addr;
  90                attr.bp_len = HW_BREAKPOINT_LEN_2;
  91                attr.bp_type = HW_BREAKPOINT_R;
  92
  93                bp = register_user_hw_breakpoint(&attr, ptrace_triggered,
  94                                                 NULL, tsk);
  95                if (IS_ERR(bp))
  96                        return PTR_ERR(bp);
  97
  98                thread->ptrace_bps[0] = bp;
  99        } else {
 100                int err;
 101
 102                attr = bp->attr;
 103                attr.bp_addr = addr;
 104                /* reenable breakpoint */
 105                attr.disabled = false;
 106                err = modify_user_hw_breakpoint(bp, &attr);
 107                if (unlikely(err))
 108                        return err;
 109        }
 110
 111        return 0;
 112}
 113
 114void user_enable_single_step(struct task_struct *child)
 115{
 116        unsigned long pc = get_stack_long(child, offsetof(struct pt_regs, pc));
 117
 118        set_tsk_thread_flag(child, TIF_SINGLESTEP);
 119
 120        if (ptrace_get_breakpoints(child) < 0)
 121                return;
 122
 123        set_single_step(child, pc);
 124        ptrace_put_breakpoints(child);
 125}
 126
 127void user_disable_single_step(struct task_struct *child)
 128{
 129        clear_tsk_thread_flag(child, TIF_SINGLESTEP);
 130}
 131
 132/*
 133 * Called by kernel/ptrace.c when detaching..
 134 *
 135 * Make sure single step bits etc are not set.
 136 */
 137void ptrace_disable(struct task_struct *child)
 138{
 139        user_disable_single_step(child);
 140}
 141
 142static int genregs_get(struct task_struct *target,
 143                       const struct user_regset *regset,
 144                       unsigned int pos, unsigned int count,
 145                       void *kbuf, void __user *ubuf)
 146{
 147        const struct pt_regs *regs = task_pt_regs(target);
 148        int ret;
 149
 150        ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 151                                  regs->regs,
 152                                  0, 16 * sizeof(unsigned long));
 153        if (!ret)
 154                /* PC, PR, SR, GBR, MACH, MACL, TRA */
 155                ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 156                                          &regs->pc,
 157                                          offsetof(struct pt_regs, pc),
 158                                          sizeof(struct pt_regs));
 159        if (!ret)
 160                ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
 161                                               sizeof(struct pt_regs), -1);
 162
 163        return ret;
 164}
 165
 166static int genregs_set(struct task_struct *target,
 167                       const struct user_regset *regset,
 168                       unsigned int pos, unsigned int count,
 169                       const void *kbuf, const void __user *ubuf)
 170{
 171        struct pt_regs *regs = task_pt_regs(target);
 172        int ret;
 173
 174        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 175                                 regs->regs,
 176                                 0, 16 * sizeof(unsigned long));
 177        if (!ret && count > 0)
 178                ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 179                                         &regs->pc,
 180                                         offsetof(struct pt_regs, pc),
 181                                         sizeof(struct pt_regs));
 182        if (!ret)
 183                ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 184                                                sizeof(struct pt_regs), -1);
 185
 186        return ret;
 187}
 188
 189#ifdef CONFIG_SH_FPU
 190int fpregs_get(struct task_struct *target,
 191               const struct user_regset *regset,
 192               unsigned int pos, unsigned int count,
 193               void *kbuf, void __user *ubuf)
 194{
 195        int ret;
 196
 197        ret = init_fpu(target);
 198        if (ret)
 199                return ret;
 200
 201        if ((boot_cpu_data.flags & CPU_HAS_FPU))
 202                return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 203                                           &target->thread.xstate->hardfpu, 0, -1);
 204
 205        return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 206                                   &target->thread.xstate->softfpu, 0, -1);
 207}
 208
 209static int fpregs_set(struct task_struct *target,
 210                       const struct user_regset *regset,
 211                       unsigned int pos, unsigned int count,
 212                       const void *kbuf, const void __user *ubuf)
 213{
 214        int ret;
 215
 216        ret = init_fpu(target);
 217        if (ret)
 218                return ret;
 219
 220        set_stopped_child_used_math(target);
 221
 222        if ((boot_cpu_data.flags & CPU_HAS_FPU))
 223                return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 224                                          &target->thread.xstate->hardfpu, 0, -1);
 225
 226        return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 227                                  &target->thread.xstate->softfpu, 0, -1);
 228}
 229
 230static int fpregs_active(struct task_struct *target,
 231                         const struct user_regset *regset)
 232{
 233        return tsk_used_math(target) ? regset->n : 0;
 234}
 235#endif
 236
 237#ifdef CONFIG_SH_DSP
 238static int dspregs_get(struct task_struct *target,
 239                       const struct user_regset *regset,
 240                       unsigned int pos, unsigned int count,
 241                       void *kbuf, void __user *ubuf)
 242{
 243        const struct pt_dspregs *regs =
 244                (struct pt_dspregs *)&target->thread.dsp_status.dsp_regs;
 245        int ret;
 246
 247        ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, regs,
 248                                  0, sizeof(struct pt_dspregs));
 249        if (!ret)
 250                ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
 251                                               sizeof(struct pt_dspregs), -1);
 252
 253        return ret;
 254}
 255
 256static int dspregs_set(struct task_struct *target,
 257                       const struct user_regset *regset,
 258                       unsigned int pos, unsigned int count,
 259                       const void *kbuf, const void __user *ubuf)
 260{
 261        struct pt_dspregs *regs =
 262                (struct pt_dspregs *)&target->thread.dsp_status.dsp_regs;
 263        int ret;
 264
 265        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, regs,
 266                                 0, sizeof(struct pt_dspregs));
 267        if (!ret)
 268                ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 269                                                sizeof(struct pt_dspregs), -1);
 270
 271        return ret;
 272}
 273
 274static int dspregs_active(struct task_struct *target,
 275                          const struct user_regset *regset)
 276{
 277        struct pt_regs *regs = task_pt_regs(target);
 278
 279        return regs->sr & SR_DSP ? regset->n : 0;
 280}
 281#endif
 282
 283const struct pt_regs_offset regoffset_table[] = {
 284        REGS_OFFSET_NAME(0),
 285        REGS_OFFSET_NAME(1),
 286        REGS_OFFSET_NAME(2),
 287        REGS_OFFSET_NAME(3),
 288        REGS_OFFSET_NAME(4),
 289        REGS_OFFSET_NAME(5),
 290        REGS_OFFSET_NAME(6),
 291        REGS_OFFSET_NAME(7),
 292        REGS_OFFSET_NAME(8),
 293        REGS_OFFSET_NAME(9),
 294        REGS_OFFSET_NAME(10),
 295        REGS_OFFSET_NAME(11),
 296        REGS_OFFSET_NAME(12),
 297        REGS_OFFSET_NAME(13),
 298        REGS_OFFSET_NAME(14),
 299        REGS_OFFSET_NAME(15),
 300        REG_OFFSET_NAME(pc),
 301        REG_OFFSET_NAME(pr),
 302        REG_OFFSET_NAME(sr),
 303        REG_OFFSET_NAME(gbr),
 304        REG_OFFSET_NAME(mach),
 305        REG_OFFSET_NAME(macl),
 306        REG_OFFSET_NAME(tra),
 307        REG_OFFSET_END,
 308};
 309
 310/*
 311 * These are our native regset flavours.
 312 */
 313enum sh_regset {
 314        REGSET_GENERAL,
 315#ifdef CONFIG_SH_FPU
 316        REGSET_FPU,
 317#endif
 318#ifdef CONFIG_SH_DSP
 319        REGSET_DSP,
 320#endif
 321};
 322
 323static const struct user_regset sh_regsets[] = {
 324        /*
 325         * Format is:
 326         *      R0 --> R15
 327         *      PC, PR, SR, GBR, MACH, MACL, TRA
 328         */
 329        [REGSET_GENERAL] = {
 330                .core_note_type = NT_PRSTATUS,
 331                .n              = ELF_NGREG,
 332                .size           = sizeof(long),
 333                .align          = sizeof(long),
 334                .get            = genregs_get,
 335                .set            = genregs_set,
 336        },
 337
 338#ifdef CONFIG_SH_FPU
 339        [REGSET_FPU] = {
 340                .core_note_type = NT_PRFPREG,
 341                .n              = sizeof(struct user_fpu_struct) / sizeof(long),
 342                .size           = sizeof(long),
 343                .align          = sizeof(long),
 344                .get            = fpregs_get,
 345                .set            = fpregs_set,
 346                .active         = fpregs_active,
 347        },
 348#endif
 349
 350#ifdef CONFIG_SH_DSP
 351        [REGSET_DSP] = {
 352                .n              = sizeof(struct pt_dspregs) / sizeof(long),
 353                .size           = sizeof(long),
 354                .align          = sizeof(long),
 355                .get            = dspregs_get,
 356                .set            = dspregs_set,
 357                .active         = dspregs_active,
 358        },
 359#endif
 360};
 361
 362static const struct user_regset_view user_sh_native_view = {
 363        .name           = "sh",
 364        .e_machine      = EM_SH,
 365        .regsets        = sh_regsets,
 366        .n              = ARRAY_SIZE(sh_regsets),
 367};
 368
 369const struct user_regset_view *task_user_regset_view(struct task_struct *task)
 370{
 371        return &user_sh_native_view;
 372}
 373
 374long arch_ptrace(struct task_struct *child, long request,
 375                 unsigned long addr, unsigned long data)
 376{
 377        unsigned long __user *datap = (unsigned long __user *)data;
 378        int ret;
 379
 380        switch (request) {
 381        /* read the word at location addr in the USER area. */
 382        case PTRACE_PEEKUSR: {
 383                unsigned long tmp;
 384
 385                ret = -EIO;
 386                if ((addr & 3) || addr < 0 ||
 387                    addr > sizeof(struct user) - 3)
 388                        break;
 389
 390                if (addr < sizeof(struct pt_regs))
 391                        tmp = get_stack_long(child, addr);
 392                else if (addr >= offsetof(struct user, fpu) &&
 393                         addr < offsetof(struct user, u_fpvalid)) {
 394                        if (!tsk_used_math(child)) {
 395                                if (addr == offsetof(struct user, fpu.fpscr))
 396                                        tmp = FPSCR_INIT;
 397                                else
 398                                        tmp = 0;
 399                        } else {
 400                                unsigned long index;
 401                                ret = init_fpu(child);
 402                                if (ret)
 403                                        break;
 404                                index = addr - offsetof(struct user, fpu);
 405                                tmp = ((unsigned long *)child->thread.xstate)
 406                                        [index >> 2];
 407                        }
 408                } else if (addr == offsetof(struct user, u_fpvalid))
 409                        tmp = !!tsk_used_math(child);
 410                else if (addr == PT_TEXT_ADDR)
 411                        tmp = child->mm->start_code;
 412                else if (addr == PT_DATA_ADDR)
 413                        tmp = child->mm->start_data;
 414                else if (addr == PT_TEXT_END_ADDR)
 415                        tmp = child->mm->end_code;
 416                else if (addr == PT_TEXT_LEN)
 417                        tmp = child->mm->end_code - child->mm->start_code;
 418                else
 419                        tmp = 0;
 420                ret = put_user(tmp, datap);
 421                break;
 422        }
 423
 424        case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
 425                ret = -EIO;
 426                if ((addr & 3) || addr < 0 ||
 427                    addr > sizeof(struct user) - 3)
 428                        break;
 429
 430                if (addr < sizeof(struct pt_regs))
 431                        ret = put_stack_long(child, addr, data);
 432                else if (addr >= offsetof(struct user, fpu) &&
 433                         addr < offsetof(struct user, u_fpvalid)) {
 434                        unsigned long index;
 435                        ret = init_fpu(child);
 436                        if (ret)
 437                                break;
 438                        index = addr - offsetof(struct user, fpu);
 439                        set_stopped_child_used_math(child);
 440                        ((unsigned long *)child->thread.xstate)
 441                                [index >> 2] = data;
 442                        ret = 0;
 443                } else if (addr == offsetof(struct user, u_fpvalid)) {
 444                        conditional_stopped_child_used_math(data, child);
 445                        ret = 0;
 446                }
 447                break;
 448
 449        case PTRACE_GETREGS:
 450                return copy_regset_to_user(child, &user_sh_native_view,
 451                                           REGSET_GENERAL,
 452                                           0, sizeof(struct pt_regs),
 453                                           datap);
 454        case PTRACE_SETREGS:
 455                return copy_regset_from_user(child, &user_sh_native_view,
 456                                             REGSET_GENERAL,
 457                                             0, sizeof(struct pt_regs),
 458                                             datap);
 459#ifdef CONFIG_SH_FPU
 460        case PTRACE_GETFPREGS:
 461                return copy_regset_to_user(child, &user_sh_native_view,
 462                                           REGSET_FPU,
 463                                           0, sizeof(struct user_fpu_struct),
 464                                           datap);
 465        case PTRACE_SETFPREGS:
 466                return copy_regset_from_user(child, &user_sh_native_view,
 467                                             REGSET_FPU,
 468                                             0, sizeof(struct user_fpu_struct),
 469                                             datap);
 470#endif
 471#ifdef CONFIG_SH_DSP
 472        case PTRACE_GETDSPREGS:
 473                return copy_regset_to_user(child, &user_sh_native_view,
 474                                           REGSET_DSP,
 475                                           0, sizeof(struct pt_dspregs),
 476                                           datap);
 477        case PTRACE_SETDSPREGS:
 478                return copy_regset_from_user(child, &user_sh_native_view,
 479                                             REGSET_DSP,
 480                                             0, sizeof(struct pt_dspregs),
 481                                             datap);
 482#endif
 483        default:
 484                ret = ptrace_request(child, request, addr, data);
 485                break;
 486        }
 487
 488        return ret;
 489}
 490
 491static inline int audit_arch(void)
 492{
 493        int arch = EM_SH;
 494
 495#ifdef CONFIG_CPU_LITTLE_ENDIAN
 496        arch |= __AUDIT_ARCH_LE;
 497#endif
 498
 499        return arch;
 500}
 501
 502asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
 503{
 504        long ret = 0;
 505
 506        secure_computing_strict(regs->regs[0]);
 507
 508        if (test_thread_flag(TIF_SYSCALL_TRACE) &&
 509            tracehook_report_syscall_entry(regs))
 510                /*
 511                 * Tracing decided this syscall should not happen.
 512                 * We'll return a bogus call number to get an ENOSYS
 513                 * error, but leave the original number in regs->regs[0].
 514                 */
 515                ret = -1L;
 516
 517        if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
 518                trace_sys_enter(regs, regs->regs[0]);
 519
 520        audit_syscall_entry(audit_arch(), regs->regs[3],
 521                            regs->regs[4], regs->regs[5],
 522                            regs->regs[6], regs->regs[7]);
 523
 524        return ret ?: regs->regs[0];
 525}
 526
 527asmlinkage void do_syscall_trace_leave(struct pt_regs *regs)
 528{
 529        int step;
 530
 531        audit_syscall_exit(regs);
 532
 533        if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
 534                trace_sys_exit(regs, regs->regs[0]);
 535
 536        step = test_thread_flag(TIF_SINGLESTEP);
 537        if (step || test_thread_flag(TIF_SYSCALL_TRACE))
 538                tracehook_report_syscall_exit(regs, step);
 539}
 540