From 7ba5782028dae7fcbc65762f8899c0998a2dce48 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Tue, 2 Jul 2024 12:10:43 +0800 Subject: [PATCH] Port seccomp acceleration to Linux 3.5.0 * etc/NEWS: Update correspondingly. * exec/Makefile.in (config-mips.m4): Don't define rule or predicate $(LOADOBJS) on it elsewhere than on MIPS. * exec/README: Direct developers to GDB. * exec/trace.c (finish_exec): Resume the tracee after reporting an error in `exec'. (after_fork): If seccomp is enabled on Android, and the kernel is 4.7 or earlier, detect whether revisions to the sequencing of seccomp events have been backported from 4.8. (exec_waitpid): Resume the process with PTRACE_CONT after receiving an unknown signal. (exec_init): Cease disabling seccomp on Android kernels earlier than 4.8. (cherry picked from commit 6b5accdc05d007ab4d1804865c1b043260006673) --- etc/NEWS | 2 +- exec/Makefile.in | 8 ++-- exec/README | 3 ++ exec/trace.c | 113 ++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 99 insertions(+), 27 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 5c08bc7550f..2b1534c31eb 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -59,7 +59,7 @@ default), the 'whitespace-cleanup' function will now add the newline. ** Process execution has been optimized on Android. The run-time performance of subprocesses on recent Android releases where a userspace executable loader is required has been optimized on -systems featuring Linux 4.8.0 and above. +systems featuring Linux 3.5.0 and above. ---------------------------------------------------------------------- diff --git a/exec/Makefile.in b/exec/Makefile.in index 6969039b0a4..7e681c0c3d8 100644 --- a/exec/Makefile.in +++ b/exec/Makefile.in @@ -90,16 +90,16 @@ ifeq ($(is_mips),yes) .s.o: $(M4) $< > $(notdir $<).s $(AS) $(ASFLAGS) $(notdir $<).s -o $@ -else -.s.o: - $(AS) $(ASFLAGS) $< -o $@ -endif # Set up dependencies for config-mips.m4. config-mips.m4: config-mips.m4.in cd $(srcdir) && ./config.status $@ $(LOADOBJS): config-mips.m4 +else +.s.o: + $(AS) $(ASFLAGS) $< -o $@ +endif # Set up rules to build libexec.a. diff --git a/exec/README b/exec/README index f7eb21cfc84..a1534503247 100644 --- a/exec/README +++ b/exec/README @@ -1,3 +1,6 @@ This directory holds the source code to a library used to replace the `execve' and `execveat' system calls, used by the Android port of Emacs to start executables without intervention from the system. + +The most edifying resource for developers will be GDB, or to be precise, +the Linux target implementations for architectures of interest. diff --git a/exec/trace.c b/exec/trace.c index dfbc255a894..2af6d867efe 100644 --- a/exec/trace.c +++ b/exec/trace.c @@ -845,7 +845,12 @@ finish_exec (struct exec_tracee *tracee, USER_REGS_STRUCT *regs) #else /* !__aarch64__ */ ptrace (PTRACE_SETREGS, tracee->pid, NULL, regs); #endif /* __aarch64__ */ - return; + + /* Continue; not much in the way of remediation is available if + either of PTRACE_SETREGS and this resumption fails. */ + ptrace ((use_seccomp_p ? PTRACE_CONT : PTRACE_SYSCALL), + tracee->pid, 0, 0); + goto error; } /* Write the loader area to the stack, followed by its size and the @@ -1858,6 +1863,10 @@ after_fork (pid_t pid) { int wstatus, rc, flags; struct exec_tracee *tracee; +#if defined HAVE_SECCOMP && __ANDROID__ + int statusarg; + USER_REGS_STRUCT regs; +#endif /* defined HAVE_SECCOMP && __ANDROID__ */ /* First, wait for something to happen to PID. */ again: @@ -1897,14 +1906,86 @@ after_fork (pid_t pid) return 1; } - /* Request that the child stop upon the next system call, or the next - filter event. */ - rc = ptrace ((use_seccomp_p ? PTRACE_CONT : PTRACE_SYSCALL), - pid, 0, 0); +#if defined HAVE_SECCOMP && __ANDROID__ + /* Certain Android kernels have received backports of those new + PTRACE_EVENT_SECCOMP semantics which were introduced in kernel + version 4.8, so that it is necessary to actively establish which + variant is in place. */ + + if (kernel_4_7_or_earlier && use_seccomp_p) + { + /* Request that the child stop upon the next `exec' system call, + one of which is assumed to always be issued by the child, as + below, but await the next stop, and examine its contents. + Anciently, the syscall-stop preceeded events marked + PTRACE_EVENT_SECCOMP, whereas this sequence is reversed in + 4.8+, and in releases with these changes backported. */ + + rc = ptrace (PTRACE_SYSCALL, pid, 0, 0); + if (rc) + return 1; + + while (true) + { + rc = waitpid (pid, &wstatus, __WALL); + if (rc != pid) + return 1; + + if (WIFSTOPPED (wstatus)) + { + /* Verify that this system call is `exec', not one issued + between PTRACE_TRACEME and `exec' intercepted by + PTRACE_SYSCALL. */ +#ifdef __aarch64__ + rc = aarch64_get_regs (pid, ®s); +#else /* !__aarch64__ */ + rc = ptrace (PTRACE_GETREGS, pid, NULL, ®s); +#endif /* __aarch64__ */ + if (rc) + return 1; + + if (regs.SYSCALL_NUM_REG == EXEC_SYSCALL) + { + statusarg = ((wstatus & 0xfff00) >> 8); + + if (statusarg == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))) + { + /* The first event to be delivered is a seccomp + stop, indicating that this is an unmodified + <4.7 kernel. Return to await the subsequent + syscall-stop, which should be received and + acted on by process_system_call. */ + rc = ptrace (PTRACE_SYSCALL, pid, 0, 0); + break; + } + else if (statusarg == (SIGTRAP | 0x80)) + { + /* Syscall-traps take priority. This is a + doctored 4.7 kernel. */ + kernel_4_7_or_earlier = false; + rc = ptrace (PTRACE_CONT, pid, 0, 0); + break; + } + } + + rc = ptrace (PTRACE_SYSCALL, pid, 0, 0); + if (rc) + return 1; + } + else + return 1; + } + } + else +#endif /* HAVE_SECCOMP && __ANDROID__ */ + /* Request that the child stop upon the next system call, or the + next filter event. */ + rc = ptrace ((use_seccomp_p ? PTRACE_CONT : PTRACE_SYSCALL), + pid, 0, 0); if (rc) return 1; - /* Enter the child in `tracing_processes'. */ + /* Enroll the child into `tracing_processes'. */ if (free_tracees) { @@ -2064,8 +2145,9 @@ exec_waitpid (pid_t pid, int *wstatus, int options) #endif /* SIGSYS */ default: - /* Continue the process until the next syscall. */ - ptrace (PTRACE_SYSCALL, pid, 0, status); + /* Resume the process as appropriate. */ + ptrace ((use_seccomp_p ? PTRACE_CONT : PTRACE_SYSCALL), + pid, 0, status); return -1; } } @@ -2117,20 +2199,7 @@ exec_init (const char *loader) else { if (major < 4 || (major == 4 && minor <= 7)) - { -#ifndef __ANDROID__ - kernel_4_7_or_earlier = true; -#else /* __ANDROID __ */ - /* Certain Android kernels have received backports of - those new PTRACE_EVENT_SECCOMP semantics which were - introduced in kernel version 4.8, so that it is - necessary to actively establish which variant is in - place. This being much too involved for code I cannot - test, simply disable seccomp on kernel releases subject - to these uncertainties. */ - use_seccomp_p = false; -#endif /* !__ANDROID__ */ - } + kernel_4_7_or_earlier = true; } } #endif /* HAVE_SECCOMP */ -- 2.39.2