import PULS_20160108
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / arch / arm64 / kernel / ptrace.c
index 85536688f753e370d114bdd6f21f95f50267e315..eff5f1a5ffb45bc6303233225ffdcaeffe73c24d 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/audit.h>
+#include <linux/compat.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/smp.h>
 #include <linux/ptrace.h>
 #include <linux/user.h>
+#include <linux/seccomp.h>
 #include <linux/security.h>
 #include <linux/init.h>
 #include <linux/signal.h>
 #include <asm/compat.h>
 #include <asm/debug-monitors.h>
 #include <asm/pgtable.h>
+#include <asm/syscall.h>
 #include <asm/traps.h>
 #include <asm/system_misc.h>
 
+#define CREATE_TRACE_POINTS
+#include <trace/events/syscalls.h>
+
 /*
  * TODO: does not yet catch signals sent when the child dies.
  * in exit.c or in signal.c.
@@ -53,28 +60,6 @@ void ptrace_disable(struct task_struct *child)
 {
 }
 
-/*
- * Handle hitting a breakpoint.
- */
-static int ptrace_break(struct pt_regs *regs)
-{
-       siginfo_t info = {
-               .si_signo = SIGTRAP,
-               .si_errno = 0,
-               .si_code  = TRAP_BRKPT,
-               .si_addr  = (void __user *)instruction_pointer(regs),
-       };
-
-       force_sig_info(SIGTRAP, &info, current);
-       return 0;
-}
-
-static int arm64_break_trap(unsigned long addr, unsigned int esr,
-                           struct pt_regs *regs)
-{
-       return ptrace_break(regs);
-}
-
 #ifdef CONFIG_HAVE_HW_BREAKPOINT
 /*
  * Handle hitting a HW-breakpoint.
@@ -539,6 +524,7 @@ static int fpr_set(struct task_struct *target, const struct user_regset *regset,
                return ret;
 
        target->thread.fpsimd_state.user_fpsimd = newstate;
+       fpsimd_flush_task_state(target);
        return ret;
 }
 
@@ -672,12 +658,20 @@ static int compat_gpr_get(struct task_struct *target,
                        reg = (void *)&task_pt_regs(target)->regs[idx];
                }
 
-               ret = copy_to_user(ubuf, reg, sizeof(compat_ulong_t));
+               if (!ubuf && kbuf) {
+                       if (i == 0 && NULL != target && target->pid == current->pid)
+                               printk(KERN_WARNING "coredump(%d) copy registers to kbuf\n", current->pid);
+                       memcpy(kbuf, reg, sizeof(compat_ulong_t));
+                       kbuf += sizeof(compat_ulong_t);
+               }
+               else {
+                       ret = copy_to_user(ubuf, reg, sizeof(compat_ulong_t));
 
-               if (ret)
-                       break;
-               else
-                       ubuf += sizeof(compat_ulong_t);
+                       if (ret)
+                               break;
+                       else
+                               ubuf += sizeof(compat_ulong_t);
+               }
        }
 
        return ret;
@@ -788,6 +782,7 @@ static int compat_vfp_set(struct task_struct *target,
                uregs->fpcr = fpscr & VFP_FPSCR_CTRL_MASK;
        }
 
+       fpsimd_flush_task_state(target);
        return ret;
 }
 
@@ -815,33 +810,6 @@ static const struct user_regset_view user_aarch32_view = {
        .regsets = aarch32_regsets, .n = ARRAY_SIZE(aarch32_regsets)
 };
 
-int aarch32_break_trap(struct pt_regs *regs)
-{
-       unsigned int instr;
-       bool bp = false;
-       void __user *pc = (void __user *)instruction_pointer(regs);
-
-       if (compat_thumb_mode(regs)) {
-               /* get 16-bit Thumb instruction */
-               get_user(instr, (u16 __user *)pc);
-               if (instr == AARCH32_BREAK_THUMB2_LO) {
-                       /* get second half of 32-bit Thumb-2 instruction */
-                       get_user(instr, (u16 __user *)(pc + 2));
-                       bp = instr == AARCH32_BREAK_THUMB2_HI;
-               } else {
-                       bp = instr == AARCH32_BREAK_THUMB;
-               }
-       } else {
-               /* 32-bit ARM instruction */
-               get_user(instr, (u32 __user *)pc);
-               bp = (instr & ~0xf0000000) == AARCH32_BREAK_ARM;
-       }
-
-       if (bp)
-               return ptrace_break(regs);
-       return 1;
-}
-
 static int compat_ptrace_read_user(struct task_struct *tsk, compat_ulong_t off,
                                   compat_ulong_t __user *ret)
 {
@@ -1110,48 +1078,93 @@ const struct user_regset_view *task_user_regset_view(struct task_struct *task)
 long arch_ptrace(struct task_struct *child, long request,
                 unsigned long addr, unsigned long data)
 {
-       return ptrace_request(child, request, addr, data);
-}
+       int ret;
 
+       switch (request) {
+               case PTRACE_SET_SYSCALL:
+                       task_pt_regs(child)->syscallno = data;
+                       ret = 0;
+                       break;
+               default:
+                       ret = ptrace_request(child, request, addr, data);
+                       break;
+       }
 
-static int __init ptrace_break_init(void)
-{
-       hook_debug_fault_code(DBG_ESR_EVT_BRK, arm64_break_trap, SIGTRAP,
-                             TRAP_BRKPT, "ptrace BRK handler");
-       return 0;
+       return ret;
 }
-core_initcall(ptrace_break_init);
 
+enum ptrace_syscall_dir {
+       PTRACE_SYSCALL_ENTER = 0,
+       PTRACE_SYSCALL_EXIT,
+};
 
-asmlinkage int syscall_trace(int dir, struct pt_regs *regs)
+static void tracehook_report_syscall(struct pt_regs *regs,
+                                    enum ptrace_syscall_dir dir)
 {
+       int regno;
        unsigned long saved_reg;
 
-       if (!test_thread_flag(TIF_SYSCALL_TRACE))
-               return regs->syscallno;
-
-       if (is_compat_task()) {
-               /* AArch32 uses ip (r12) for scratch */
-               saved_reg = regs->regs[12];
-               regs->regs[12] = dir;
-       } else {
-               /*
-                * Save X7. X7 is used to denote syscall entry/exit:
-                *   X7 = 0 -> entry, = 1 -> exit
-                */
-               saved_reg = regs->regs[7];
-               regs->regs[7] = dir;
-       }
+       /*
+        * A scratch register (ip(r12) on AArch32, x7 on AArch64) is
+        * used to denote syscall entry/exit:
+        */
+       regno = (is_compat_task() ? 12 : 7);
+       saved_reg = regs->regs[regno];
+       regs->regs[regno] = dir;
 
-       if (dir)
+       if (dir == PTRACE_SYSCALL_EXIT)
                tracehook_report_syscall_exit(regs, 0);
        else if (tracehook_report_syscall_entry(regs))
                regs->syscallno = ~0UL;
 
-       if (is_compat_task())
-               regs->regs[12] = saved_reg;
-       else
-               regs->regs[7] = saved_reg;
+       regs->regs[regno] = saved_reg;
+}
+
+asmlinkage int syscall_trace_enter(struct pt_regs *regs)
+{
+       unsigned int saved_syscallno = regs->syscallno;
+
+       /* Do the secure computing check first; failures should be fast. */
+       if (secure_computing(regs->syscallno) == -1)
+               return RET_SKIP_SYSCALL_TRACE;
+
+       if (test_thread_flag(TIF_SYSCALL_TRACE))
+               tracehook_report_syscall(regs, PTRACE_SYSCALL_ENTER);
+
+       if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
+               trace_sys_enter(regs, regs->syscallno);
+
+       if (IS_SKIP_SYSCALL(regs->syscallno)) {
+               /*
+                * RESTRICTION: we can't modify a return value of user
+                * issued syscall(-1) here. In order to ease this flavor,
+                * we need to treat whatever value in x0 as a return value,
+                * but this might result in a bogus value being returned.
+                */
+               /*
+                * NOTE: syscallno may also be set to -1 if fatal signal is
+                * detected in tracehook_report_syscall_entry(), but since
+                * a value set to x0 here is not used in this case, we may
+                * neglect the case.
+                */
+               if (!test_thread_flag(TIF_SYSCALL_TRACE) ||
+                               (IS_SKIP_SYSCALL(saved_syscallno)))
+                       regs->regs[0] = -ENOSYS;
+       }
+
+       audit_syscall_entry(syscall_get_arch(), regs->syscallno,
+               regs->orig_x0, regs->regs[1], regs->regs[2], regs->regs[3]);
 
        return regs->syscallno;
 }
+
+asmlinkage void syscall_trace_exit(struct pt_regs *regs)
+{
+       audit_syscall_exit(regs);
+
+       if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
+               trace_sys_exit(regs, regs_return_value(regs));
+
+       if (test_thread_flag(TIF_SYSCALL_TRACE))
+               tracehook_report_syscall(regs, PTRACE_SYSCALL_EXIT);
+}