From: Po Lu Date: Thu, 14 Mar 2024 05:45:48 +0000 (+0800) Subject: Improve /proc/self/exe substitution on Android X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=ead1a200e01649dae9a72d9be7e084e5ecd63a17;p=emacs.git Improve /proc/self/exe substitution on Android * exec/configure.ac (USER_SWORD): New macro. * exec/exec.c (format_pid): Export this function. * exec/exec.h: * exec/trace.c (canon_path): New function. (handle_readlinkat, handle_openat): Test complete file name against /proc/self/exe, and further check for /proc/pid/exe. (cherry picked from commit 30bc867aecc59265b6e315acf459f8d79c423bca) --- diff --git a/exec/configure.ac b/exec/configure.ac index 317250332cb..a473a1dc633 100644 --- a/exec/configure.ac +++ b/exec/configure.ac @@ -122,6 +122,7 @@ AH_TEMPLATE([SYSCALL_RET_REG], [Define to register holding value of system calls AH_TEMPLATE([STACK_POINTER], [Define to register holding the stack pointer.]) AH_TEMPLATE([EXEC_SYSCALL], [Define to number of the `exec' system call.]) AH_TEMPLATE([USER_WORD], [Define to word type used by tracees.]) +AH_TEMPLATE([USER_SWORD], [Define to signed word type used by tracees.]) AH_TEMPLATE([EXEC_64], [Define to 1 if the system utilizes 64-bit ELF.]) AH_TEMPLATE([STACK_GROWS_DOWNWARDS], [Define to 1 if the stack grows downwards.]) AH_TEMPLATE([ABI_RED_ZONE], [Define to number of reserved bytes past the stack frame.]) @@ -251,6 +252,7 @@ AS_CASE([$host], [x86_64-*linux*], AC_DEFINE([STACK_POINTER], [rsp]) AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) AC_DEFINE([USER_WORD], [uintptr_t]) + AC_DEFINE([USER_SWORD], [intptr_t]) AC_DEFINE([EXEC_64], [1]) AC_DEFINE([ABI_RED_ZONE], [128]) AC_DEFINE([EXECUTABLE_BASE], [0x555555554000]) @@ -283,6 +285,7 @@ AS_CASE([$host], [x86_64-*linux*], AC_DEFINE([STACK_POINTER], [esp]) AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) AC_DEFINE([USER_WORD], [uintptr_t]) + AC_DEFINE([USER_SWORD], [intptr_t]) AC_DEFINE([EXECUTABLE_BASE], [0x0f000000]) AC_DEFINE([INTERPRETER_BASE], [0xaf000000]) AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) @@ -313,6 +316,7 @@ AS_CASE([$host], [x86_64-*linux*], AC_DEFINE([STACK_POINTER], [sp]) AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) AC_DEFINE([USER_WORD], [uintptr_t]) + AC_DEFINE([USER_SWORD], [intptr_t]) AC_DEFINE([EXEC_64], [1]) AC_DEFINE([EXECUTABLE_BASE], [0x3000000000]) AC_DEFINE([INTERPRETER_BASE], [0x3f00000000]) @@ -344,6 +348,7 @@ AS_CASE([$host], [x86_64-*linux*], AC_DEFINE([STACK_POINTER], [[uregs[13]]]) AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) AC_DEFINE([USER_WORD], [uintptr_t]) + AC_DEFINE([USER_SWORD], [intptr_t]) AC_DEFINE([EXECUTABLE_BASE], [0x0f000000]) AC_DEFINE([INTERPRETER_BASE], [0x1f000000]) AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) @@ -368,6 +373,7 @@ AS_CASE([$host], [x86_64-*linux*], AC_DEFINE([STACK_POINTER], [[uregs[13]]]) AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) AC_DEFINE([USER_WORD], [uintptr_t]) + AC_DEFINE([USER_SWORD], [intptr_t]) AC_DEFINE([EXECUTABLE_BASE], [0x0f000000]) AC_DEFINE([INTERPRETER_BASE], [0x1f000000]) AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) @@ -398,6 +404,7 @@ AS_CASE([$host], [x86_64-*linux*], AC_DEFINE([STACK_POINTER], [[gregs[29]]]) # sp AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) AC_DEFINE([USER_WORD], [uintptr_t]) + AC_DEFINE([USER_SWORD], [intptr_t]) AC_DEFINE([EXECUTABLE_BASE], [0x0f000000]) AC_DEFINE([INTERPRETER_BASE], [0x1f000000]) AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) @@ -427,6 +434,7 @@ AS_CASE([$host], [x86_64-*linux*], AC_DEFINE([STACK_POINTER], [[gregs[29]]]) # sp AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) AC_DEFINE([USER_WORD], [uintptr_t]) + AC_DEFINE([USER_SWORD], [intptr_t]) AC_DEFINE([EXEC_64], [1]) AC_DEFINE([EXECUTABLE_BASE], [0x400000]) AC_DEFINE([INTERPRETER_BASE], [0x3f00000000]) diff --git a/exec/exec.c b/exec/exec.c index 254a983f25f..cbe22d4f18c 100644 --- a/exec/exec.c +++ b/exec/exec.c @@ -865,7 +865,7 @@ insert_args (struct exec_tracee *tracee, USER_REGS_STRUCT *regs, result in *IN, and return a pointer to the byte after the result. REM should be NULL. */ -static char * +char * format_pid (char *in, unsigned int pid) { unsigned int digits[32], *fill; diff --git a/exec/exec.h b/exec/exec.h index ad1b50276c8..3ce06c35311 100644 --- a/exec/exec.h +++ b/exec/exec.h @@ -180,6 +180,7 @@ extern int aarch64_set_regs (pid_t, USER_REGS_STRUCT *, bool); +extern char *format_pid (char *, unsigned int); extern USER_WORD user_alloca (struct exec_tracee *, USER_REGS_STRUCT *, USER_REGS_STRUCT *, USER_WORD); extern int user_copy (struct exec_tracee *, const unsigned char *, diff --git a/exec/trace.c b/exec/trace.c index a7cbda54d68..64dadc092c2 100644 --- a/exec/trace.c +++ b/exec/trace.c @@ -31,6 +31,7 @@ along with GNU Emacs. If not, see . */ #include #include #include +#include #include "exec.h" @@ -894,6 +895,68 @@ handle_exec (struct exec_tracee *tracee, USER_REGS_STRUCT *regs) return 3; } +/* Modify BUFFER, of size SIZE, so that it holds the absolute name of + the file identified by BUFFER, relative to the current working + directory of TRACEE if FD be AT_FDCWD, or the file referenced by FD + otherwise. + + Value is 1 if this information is unavailable (of which there are + variety of causes), and 0 on success. */ + +static int +canon_path (struct exec_tracee *tracee, int fd, char *buffer, + ptrdiff_t size) +{ + char link[sizeof "/proc//fd/" + 48], *p; /* Or /proc/pid/cwd. */ + char target[PATH_MAX]; + ssize_t rc, length; + + if (buffer[0] == '/') + /* Absolute file name; return immediately. */ + return 0; + else if (fd == AT_FDCWD) + { + p = stpcpy (link, "/proc/"); + p = format_pid (p, tracee->pid); + stpcpy (p, "/cwd"); + } + else if (fd < 0) + /* Invalid file descriptor. */ + return 1; + else + { + p = stpcpy (link, "/proc/"); + p = format_pid (p, tracee->pid); + p = stpcpy (p, "/fd/"); + format_pid (p, fd); + } + + /* Read LINK's target, and should it be oversized, punt. */ + rc = readlink (link, target, PATH_MAX); + if (rc < 0 || rc >= PATH_MAX) + return 1; + + /* Consider the amount by which BUFFER's existing contents should be + displaced. */ + + length = strlen (buffer) + 1; + if ((length + rc + (target[rc - 1] != '/')) > size) + /* Punt if this would overflow. */ + return 1; + + memmove ((buffer + rc + (target[rc - 1] != '/')), + buffer, length); + + /* Copy the new file name into BUFFER. */ + memcpy (buffer, target, rc); + + /* Insert separator in between if need be. */ + if (target[rc - 1] != '/') + buffer[rc] = '/'; + + return 0; +} + /* Handle a `readlink' or `readlinkat' system call. CALLNO is the system call number, and REGS are the current user @@ -924,22 +987,26 @@ handle_readlinkat (USER_WORD callno, USER_REGS_STRUCT *regs, char buffer[PATH_MAX + 1]; USER_WORD address, return_buffer, size; size_t length; + char proc_pid_exe[sizeof "/proc//exe" + 24], *p; + int dirfd; /* Read the file name. */ #ifdef READLINK_SYSCALL if (callno == READLINK_SYSCALL) { - address = regs->SYSCALL_ARG_REG; + dirfd = AT_FDCWD; + address = regs->SYSCALL_ARG_REG; return_buffer = regs->SYSCALL_ARG1_REG; - size = regs->SYSCALL_ARG2_REG; + size = regs->SYSCALL_ARG2_REG; } else #endif /* READLINK_SYSCALL */ { - address = regs->SYSCALL_ARG1_REG; + dirfd = (USER_SWORD) regs->SYSCALL_ARG_REG; + address = regs->SYSCALL_ARG1_REG; return_buffer = regs->SYSCALL_ARG2_REG; - size = regs->SYSCALL_ARG3_REG; + size = regs->SYSCALL_ARG3_REG; } read_memory (tracee, buffer, PATH_MAX, address); @@ -952,12 +1019,25 @@ handle_readlinkat (USER_WORD callno, USER_REGS_STRUCT *regs, return 1; } - /* Now check if the caller is looking for /proc/self/exe. + /* Expand BUFFER into an absolute file name. TODO: + AT_SYMLINK_FOLLOW? */ + + if (canon_path (tracee, dirfd, buffer, sizeof buffer)) + return 0; + + /* Now check if the caller is looking for /proc/self/exe or its + equivalent with the PID made explicit. dirfd can be ignored, as for now only absolute file names are handled. FIXME. */ - if (strcmp (buffer, "/proc/self/exe") || !tracee->exec_file) + p = stpcpy (proc_pid_exe, "/proc/"); + p = format_pid (p, tracee->pid); + stpcpy (p, "/exe"); + + if ((strcmp (buffer, "/proc/self/exe") + && strcmp (buffer, proc_pid_exe)) + || !tracee->exec_file) return 0; /* Copy over tracee->exec_file. Truncate it to PATH_MAX, length, or @@ -1004,15 +1084,23 @@ handle_openat (USER_WORD callno, USER_REGS_STRUCT *regs, USER_WORD address; size_t length; USER_REGS_STRUCT original; + char proc_pid_exe[sizeof "/proc//exe" + 24], *p; + int dirfd; /* Read the file name. */ #ifdef OPEN_SYSCALL if (callno == OPEN_SYSCALL) - address = regs->SYSCALL_ARG_REG; + { + dirfd = AT_FDCWD; + address = regs->SYSCALL_ARG_REG; + } else #endif /* OPEN_SYSCALL */ - address = regs->SYSCALL_ARG1_REG; + { + dirfd = (USER_SWORD) regs->SYSCALL_ARG_REG; + address = regs->SYSCALL_ARG1_REG; + } /* Read the file name into the buffer and verify that it is NULL terminated. */ @@ -1024,12 +1112,25 @@ handle_openat (USER_WORD callno, USER_REGS_STRUCT *regs, return 1; } - /* Now check if the caller is looking for /proc/self/exe. + /* Expand BUFFER into an absolute file name. TODO: + AT_SYMLINK_FOLLOW? */ + + if (canon_path (tracee, dirfd, buffer, sizeof buffer)) + return 0; + + /* Now check if the caller is looking for /proc/self/exe or its + equivalent with the PID made explicit. dirfd can be ignored, as for now only absolute file names are handled. FIXME. */ - if (strcmp (buffer, "/proc/self/exe") || !tracee->exec_file) + p = stpcpy (proc_pid_exe, "/proc/"); + p = format_pid (p, tracee->pid); + stpcpy (p, "/exe"); + + if ((strcmp (buffer, "/proc/self/exe") + && strcmp (buffer, proc_pid_exe)) + || !tracee->exec_file) return 0; /* Copy over tracee->exec_file. This doesn't correctly handle the