|
@@ -1441,20 +1441,126 @@ void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs,
|
|
force_sig_info(SIGTRAP, &info, tsk);
|
|
force_sig_info(SIGTRAP, &info, tsk);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void do_audit_syscall_entry(struct pt_regs *regs, u32 arch)
|
|
|
|
+{
|
|
|
|
+#ifdef CONFIG_X86_64
|
|
|
|
+ if (arch == AUDIT_ARCH_X86_64) {
|
|
|
|
+ audit_syscall_entry(arch, regs->orig_ax, regs->di,
|
|
|
|
+ regs->si, regs->dx, regs->r10);
|
|
|
|
+ } else
|
|
|
|
+#endif
|
|
|
|
+ {
|
|
|
|
+ audit_syscall_entry(arch, regs->orig_ax, regs->bx,
|
|
|
|
+ regs->cx, regs->dx, regs->si);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
- * We must return the syscall number to actually look up in the table.
|
|
|
|
- * This can be -1L to skip running any syscall at all.
|
|
|
|
|
|
+ * We can return 0 to resume the syscall or anything else to go to phase
|
|
|
|
+ * 2. If we resume the syscall, we need to put something appropriate in
|
|
|
|
+ * regs->orig_ax.
|
|
|
|
+ *
|
|
|
|
+ * NB: We don't have full pt_regs here, but regs->orig_ax and regs->ax
|
|
|
|
+ * are fully functional.
|
|
|
|
+ *
|
|
|
|
+ * For phase 2's benefit, our return value is:
|
|
|
|
+ * 0: resume the syscall
|
|
|
|
+ * 1: go to phase 2; no seccomp phase 2 needed
|
|
|
|
+ * anything else: go to phase 2; pass return value to seccomp
|
|
*/
|
|
*/
|
|
-long syscall_trace_enter(struct pt_regs *regs)
|
|
|
|
|
|
+unsigned long syscall_trace_enter_phase1(struct pt_regs *regs, u32 arch)
|
|
{
|
|
{
|
|
- long ret = 0;
|
|
|
|
|
|
+ unsigned long ret = 0;
|
|
|
|
+ u32 work;
|
|
|
|
+
|
|
|
|
+ BUG_ON(regs != task_pt_regs(current));
|
|
|
|
+
|
|
|
|
+ work = ACCESS_ONCE(current_thread_info()->flags) &
|
|
|
|
+ _TIF_WORK_SYSCALL_ENTRY;
|
|
|
|
|
|
/*
|
|
/*
|
|
* If TIF_NOHZ is set, we are required to call user_exit() before
|
|
* If TIF_NOHZ is set, we are required to call user_exit() before
|
|
* doing anything that could touch RCU.
|
|
* doing anything that could touch RCU.
|
|
*/
|
|
*/
|
|
- if (test_thread_flag(TIF_NOHZ))
|
|
|
|
|
|
+ if (work & _TIF_NOHZ) {
|
|
user_exit();
|
|
user_exit();
|
|
|
|
+ work &= ~TIF_NOHZ;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_SECCOMP
|
|
|
|
+ /*
|
|
|
|
+ * Do seccomp first -- it should minimize exposure of other
|
|
|
|
+ * code, and keeping seccomp fast is probably more valuable
|
|
|
|
+ * than the rest of this.
|
|
|
|
+ */
|
|
|
|
+ if (work & _TIF_SECCOMP) {
|
|
|
|
+ struct seccomp_data sd;
|
|
|
|
+
|
|
|
|
+ sd.arch = arch;
|
|
|
|
+ sd.nr = regs->orig_ax;
|
|
|
|
+ sd.instruction_pointer = regs->ip;
|
|
|
|
+#ifdef CONFIG_X86_64
|
|
|
|
+ if (arch == AUDIT_ARCH_X86_64) {
|
|
|
|
+ sd.args[0] = regs->di;
|
|
|
|
+ sd.args[1] = regs->si;
|
|
|
|
+ sd.args[2] = regs->dx;
|
|
|
|
+ sd.args[3] = regs->r10;
|
|
|
|
+ sd.args[4] = regs->r8;
|
|
|
|
+ sd.args[5] = regs->r9;
|
|
|
|
+ } else
|
|
|
|
+#endif
|
|
|
|
+ {
|
|
|
|
+ sd.args[0] = regs->bx;
|
|
|
|
+ sd.args[1] = regs->cx;
|
|
|
|
+ sd.args[2] = regs->dx;
|
|
|
|
+ sd.args[3] = regs->si;
|
|
|
|
+ sd.args[4] = regs->di;
|
|
|
|
+ sd.args[5] = regs->bp;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ BUILD_BUG_ON(SECCOMP_PHASE1_OK != 0);
|
|
|
|
+ BUILD_BUG_ON(SECCOMP_PHASE1_SKIP != 1);
|
|
|
|
+
|
|
|
|
+ ret = seccomp_phase1(&sd);
|
|
|
|
+ if (ret == SECCOMP_PHASE1_SKIP) {
|
|
|
|
+ regs->orig_ax = -1;
|
|
|
|
+ ret = 0;
|
|
|
|
+ } else if (ret != SECCOMP_PHASE1_OK) {
|
|
|
|
+ return ret; /* Go directly to phase 2 */
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ work &= ~_TIF_SECCOMP;
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+ /* Do our best to finish without phase 2. */
|
|
|
|
+ if (work == 0)
|
|
|
|
+ return ret; /* seccomp and/or nohz only (ret == 0 here) */
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_AUDITSYSCALL
|
|
|
|
+ if (work == _TIF_SYSCALL_AUDIT) {
|
|
|
|
+ /*
|
|
|
|
+ * If there is no more work to be done except auditing,
|
|
|
|
+ * then audit in phase 1. Phase 2 always audits, so, if
|
|
|
|
+ * we audit here, then we can't go on to phase 2.
|
|
|
|
+ */
|
|
|
|
+ do_audit_syscall_entry(regs, arch);
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+ return 1; /* Something is enabled that we can't handle in phase 1 */
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Returns the syscall nr to run (which should match regs->orig_ax). */
|
|
|
|
+long syscall_trace_enter_phase2(struct pt_regs *regs, u32 arch,
|
|
|
|
+ unsigned long phase1_result)
|
|
|
|
+{
|
|
|
|
+ long ret = 0;
|
|
|
|
+ u32 work = ACCESS_ONCE(current_thread_info()->flags) &
|
|
|
|
+ _TIF_WORK_SYSCALL_ENTRY;
|
|
|
|
+
|
|
|
|
+ BUG_ON(regs != task_pt_regs(current));
|
|
|
|
|
|
/*
|
|
/*
|
|
* If we stepped into a sysenter/syscall insn, it trapped in
|
|
* If we stepped into a sysenter/syscall insn, it trapped in
|
|
@@ -1463,17 +1569,21 @@ long syscall_trace_enter(struct pt_regs *regs)
|
|
* do_debug() and we need to set it again to restore the user
|
|
* do_debug() and we need to set it again to restore the user
|
|
* state. If we entered on the slow path, TF was already set.
|
|
* state. If we entered on the slow path, TF was already set.
|
|
*/
|
|
*/
|
|
- if (test_thread_flag(TIF_SINGLESTEP))
|
|
|
|
|
|
+ if (work & _TIF_SINGLESTEP)
|
|
regs->flags |= X86_EFLAGS_TF;
|
|
regs->flags |= X86_EFLAGS_TF;
|
|
|
|
|
|
- /* do the secure computing check first */
|
|
|
|
- if (secure_computing()) {
|
|
|
|
|
|
+#ifdef CONFIG_SECCOMP
|
|
|
|
+ /*
|
|
|
|
+ * Call seccomp_phase2 before running the other hooks so that
|
|
|
|
+ * they can see any changes made by a seccomp tracer.
|
|
|
|
+ */
|
|
|
|
+ if (phase1_result > 1 && seccomp_phase2(phase1_result)) {
|
|
/* seccomp failures shouldn't expose any additional code. */
|
|
/* seccomp failures shouldn't expose any additional code. */
|
|
- ret = -1L;
|
|
|
|
- goto out;
|
|
|
|
|
|
+ return -1;
|
|
}
|
|
}
|
|
|
|
+#endif
|
|
|
|
|
|
- if (unlikely(test_thread_flag(TIF_SYSCALL_EMU)))
|
|
|
|
|
|
+ if (unlikely(work & _TIF_SYSCALL_EMU))
|
|
ret = -1L;
|
|
ret = -1L;
|
|
|
|
|
|
if ((ret || test_thread_flag(TIF_SYSCALL_TRACE)) &&
|
|
if ((ret || test_thread_flag(TIF_SYSCALL_TRACE)) &&
|
|
@@ -1483,23 +1593,22 @@ long syscall_trace_enter(struct pt_regs *regs)
|
|
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
|
|
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
|
|
trace_sys_enter(regs, regs->orig_ax);
|
|
trace_sys_enter(regs, regs->orig_ax);
|
|
|
|
|
|
- if (is_ia32_task())
|
|
|
|
- audit_syscall_entry(AUDIT_ARCH_I386,
|
|
|
|
- regs->orig_ax,
|
|
|
|
- regs->bx, regs->cx,
|
|
|
|
- regs->dx, regs->si);
|
|
|
|
-#ifdef CONFIG_X86_64
|
|
|
|
- else
|
|
|
|
- audit_syscall_entry(AUDIT_ARCH_X86_64,
|
|
|
|
- regs->orig_ax,
|
|
|
|
- regs->di, regs->si,
|
|
|
|
- regs->dx, regs->r10);
|
|
|
|
-#endif
|
|
|
|
|
|
+ do_audit_syscall_entry(regs, arch);
|
|
|
|
|
|
-out:
|
|
|
|
return ret ?: regs->orig_ax;
|
|
return ret ?: regs->orig_ax;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+long syscall_trace_enter(struct pt_regs *regs)
|
|
|
|
+{
|
|
|
|
+ u32 arch = is_ia32_task() ? AUDIT_ARCH_I386 : AUDIT_ARCH_X86_64;
|
|
|
|
+ unsigned long phase1_result = syscall_trace_enter_phase1(regs, arch);
|
|
|
|
+
|
|
|
|
+ if (phase1_result == 0)
|
|
|
|
+ return regs->orig_ax;
|
|
|
|
+ else
|
|
|
|
+ return syscall_trace_enter_phase2(regs, arch, phase1_result);
|
|
|
|
+}
|
|
|
|
+
|
|
void syscall_trace_leave(struct pt_regs *regs)
|
|
void syscall_trace_leave(struct pt_regs *regs)
|
|
{
|
|
{
|
|
bool step;
|
|
bool step;
|