1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-06 06:20:55 -08:00

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.
This commit is contained in:
Po Lu 2024-03-14 13:45:48 +08:00
parent db5c8bda63
commit 30bc867aec
4 changed files with 121 additions and 11 deletions

View file

@ -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([STACK_POINTER], [Define to register holding the stack pointer.])
AH_TEMPLATE([EXEC_SYSCALL], [Define to number of the `exec' system call.]) 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_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([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([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.]) 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([STACK_POINTER], [rsp])
AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
AC_DEFINE([USER_WORD], [uintptr_t]) AC_DEFINE([USER_WORD], [uintptr_t])
AC_DEFINE([USER_SWORD], [intptr_t])
AC_DEFINE([EXEC_64], [1]) AC_DEFINE([EXEC_64], [1])
AC_DEFINE([ABI_RED_ZONE], [128]) AC_DEFINE([ABI_RED_ZONE], [128])
AC_DEFINE([EXECUTABLE_BASE], [0x555555554000]) AC_DEFINE([EXECUTABLE_BASE], [0x555555554000])
@ -283,6 +285,7 @@ AS_CASE([$host], [x86_64-*linux*],
AC_DEFINE([STACK_POINTER], [esp]) AC_DEFINE([STACK_POINTER], [esp])
AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
AC_DEFINE([USER_WORD], [uintptr_t]) AC_DEFINE([USER_WORD], [uintptr_t])
AC_DEFINE([USER_SWORD], [intptr_t])
AC_DEFINE([EXECUTABLE_BASE], [0x0f000000]) AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
AC_DEFINE([INTERPRETER_BASE], [0xaf000000]) AC_DEFINE([INTERPRETER_BASE], [0xaf000000])
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
@ -313,6 +316,7 @@ AS_CASE([$host], [x86_64-*linux*],
AC_DEFINE([STACK_POINTER], [sp]) AC_DEFINE([STACK_POINTER], [sp])
AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
AC_DEFINE([USER_WORD], [uintptr_t]) AC_DEFINE([USER_WORD], [uintptr_t])
AC_DEFINE([USER_SWORD], [intptr_t])
AC_DEFINE([EXEC_64], [1]) AC_DEFINE([EXEC_64], [1])
AC_DEFINE([EXECUTABLE_BASE], [0x3000000000]) AC_DEFINE([EXECUTABLE_BASE], [0x3000000000])
AC_DEFINE([INTERPRETER_BASE], [0x3f00000000]) AC_DEFINE([INTERPRETER_BASE], [0x3f00000000])
@ -344,6 +348,7 @@ AS_CASE([$host], [x86_64-*linux*],
AC_DEFINE([STACK_POINTER], [[uregs[13]]]) AC_DEFINE([STACK_POINTER], [[uregs[13]]])
AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
AC_DEFINE([USER_WORD], [uintptr_t]) AC_DEFINE([USER_WORD], [uintptr_t])
AC_DEFINE([USER_SWORD], [intptr_t])
AC_DEFINE([EXECUTABLE_BASE], [0x0f000000]) AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
AC_DEFINE([INTERPRETER_BASE], [0x1f000000]) AC_DEFINE([INTERPRETER_BASE], [0x1f000000])
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
@ -368,6 +373,7 @@ AS_CASE([$host], [x86_64-*linux*],
AC_DEFINE([STACK_POINTER], [[uregs[13]]]) AC_DEFINE([STACK_POINTER], [[uregs[13]]])
AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
AC_DEFINE([USER_WORD], [uintptr_t]) AC_DEFINE([USER_WORD], [uintptr_t])
AC_DEFINE([USER_SWORD], [intptr_t])
AC_DEFINE([EXECUTABLE_BASE], [0x0f000000]) AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
AC_DEFINE([INTERPRETER_BASE], [0x1f000000]) AC_DEFINE([INTERPRETER_BASE], [0x1f000000])
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) 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([STACK_POINTER], [[gregs[29]]]) # sp
AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
AC_DEFINE([USER_WORD], [uintptr_t]) AC_DEFINE([USER_WORD], [uintptr_t])
AC_DEFINE([USER_SWORD], [intptr_t])
AC_DEFINE([EXECUTABLE_BASE], [0x0f000000]) AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
AC_DEFINE([INTERPRETER_BASE], [0x1f000000]) AC_DEFINE([INTERPRETER_BASE], [0x1f000000])
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) 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([STACK_POINTER], [[gregs[29]]]) # sp
AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
AC_DEFINE([USER_WORD], [uintptr_t]) AC_DEFINE([USER_WORD], [uintptr_t])
AC_DEFINE([USER_SWORD], [intptr_t])
AC_DEFINE([EXEC_64], [1]) AC_DEFINE([EXEC_64], [1])
AC_DEFINE([EXECUTABLE_BASE], [0x400000]) AC_DEFINE([EXECUTABLE_BASE], [0x400000])
AC_DEFINE([INTERPRETER_BASE], [0x3f00000000]) AC_DEFINE([INTERPRETER_BASE], [0x3f00000000])

View file

@ -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 in *IN, and return a pointer to the byte after the
result. REM should be NULL. */ result. REM should be NULL. */
static char * char *
format_pid (char *in, unsigned int pid) format_pid (char *in, unsigned int pid)
{ {
unsigned int digits[32], *fill; unsigned int digits[32], *fill;

View file

@ -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 *, extern USER_WORD user_alloca (struct exec_tracee *, USER_REGS_STRUCT *,
USER_REGS_STRUCT *, USER_WORD); USER_REGS_STRUCT *, USER_WORD);
extern int user_copy (struct exec_tracee *, const unsigned char *, extern int user_copy (struct exec_tracee *, const unsigned char *,

View file

@ -31,6 +31,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include "exec.h" #include "exec.h"
@ -894,6 +895,68 @@ handle_exec (struct exec_tracee *tracee, USER_REGS_STRUCT *regs)
return 3; 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. /* Handle a `readlink' or `readlinkat' system call.
CALLNO is the system call number, and REGS are the current user CALLNO is the system call number, and REGS are the current user
@ -924,12 +987,15 @@ handle_readlinkat (USER_WORD callno, USER_REGS_STRUCT *regs,
char buffer[PATH_MAX + 1]; char buffer[PATH_MAX + 1];
USER_WORD address, return_buffer, size; USER_WORD address, return_buffer, size;
size_t length; size_t length;
char proc_pid_exe[sizeof "/proc//exe" + 24], *p;
int dirfd;
/* Read the file name. */ /* Read the file name. */
#ifdef READLINK_SYSCALL #ifdef READLINK_SYSCALL
if (callno == READLINK_SYSCALL) if (callno == READLINK_SYSCALL)
{ {
dirfd = AT_FDCWD;
address = regs->SYSCALL_ARG_REG; address = regs->SYSCALL_ARG_REG;
return_buffer = regs->SYSCALL_ARG1_REG; return_buffer = regs->SYSCALL_ARG1_REG;
size = regs->SYSCALL_ARG2_REG; size = regs->SYSCALL_ARG2_REG;
@ -937,6 +1003,7 @@ handle_readlinkat (USER_WORD callno, USER_REGS_STRUCT *regs,
else else
#endif /* READLINK_SYSCALL */ #endif /* READLINK_SYSCALL */
{ {
dirfd = (USER_SWORD) regs->SYSCALL_ARG_REG;
address = regs->SYSCALL_ARG1_REG; address = regs->SYSCALL_ARG1_REG;
return_buffer = regs->SYSCALL_ARG2_REG; return_buffer = regs->SYSCALL_ARG2_REG;
size = regs->SYSCALL_ARG3_REG; size = regs->SYSCALL_ARG3_REG;
@ -952,12 +1019,25 @@ handle_readlinkat (USER_WORD callno, USER_REGS_STRUCT *regs,
return 1; 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 dirfd can be ignored, as for now only absolute file names are
handled. FIXME. */ 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; return 0;
/* Copy over tracee->exec_file. Truncate it to PATH_MAX, length, or /* 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; USER_WORD address;
size_t length; size_t length;
USER_REGS_STRUCT original; USER_REGS_STRUCT original;
char proc_pid_exe[sizeof "/proc//exe" + 24], *p;
int dirfd;
/* Read the file name. */ /* Read the file name. */
#ifdef OPEN_SYSCALL #ifdef OPEN_SYSCALL
if (callno == OPEN_SYSCALL) if (callno == OPEN_SYSCALL)
{
dirfd = AT_FDCWD;
address = regs->SYSCALL_ARG_REG; address = regs->SYSCALL_ARG_REG;
}
else else
#endif /* OPEN_SYSCALL */ #endif /* OPEN_SYSCALL */
{
dirfd = (USER_SWORD) regs->SYSCALL_ARG_REG;
address = regs->SYSCALL_ARG1_REG; address = regs->SYSCALL_ARG1_REG;
}
/* Read the file name into the buffer and verify that it is NULL /* Read the file name into the buffer and verify that it is NULL
terminated. */ terminated. */
@ -1024,12 +1112,25 @@ handle_openat (USER_WORD callno, USER_REGS_STRUCT *regs,
return 1; 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 dirfd can be ignored, as for now only absolute file names are
handled. FIXME. */ 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; return 0;
/* Copy over tracee->exec_file. This doesn't correctly handle the /* Copy over tracee->exec_file. This doesn't correctly handle the