summaryrefslogtreecommitdiff
path: root/exec
diff options
context:
space:
mode:
authorPo Lu <luangruo@yahoo.com>2024-03-14 13:45:48 +0800
committerPo Lu <luangruo@yahoo.com>2024-03-14 13:45:48 +0800
commit30bc867aecc59265b6e315acf459f8d79c423bca (patch)
tree501c2c810e57464d20cf55d7e693d5f2f3ae2ef0 /exec
parentdb5c8bda638468f8798c974f4ef4ab3905dbddd3 (diff)
downloademacs-30bc867aecc59265b6e315acf459f8d79c423bca.tar.gz
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.
Diffstat (limited to 'exec')
-rw-r--r--exec/configure.ac8
-rw-r--r--exec/exec.c2
-rw-r--r--exec/exec.h1
-rw-r--r--exec/trace.c121
4 files changed, 121 insertions, 11 deletions
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 <https://www.gnu.org/licenses/>. */
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
+#include <fcntl.h>
#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