]> git.eshelyaron.com Git - emacs.git/commitdiff
Improve /proc/self/exe substitution on Android
authorPo Lu <luangruo@yahoo.com>
Thu, 14 Mar 2024 05:45:48 +0000 (13:45 +0800)
committerEshel Yaron <me@eshelyaron.com>
Fri, 15 Mar 2024 08:59:14 +0000 (09:59 +0100)
* 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)

exec/configure.ac
exec/exec.c
exec/exec.h
exec/trace.c

index 317250332cb298ee94e65ba8297856450dd1cd93..a473a1dc6336e93ef4efc912d739d2b6a580d91e 100644 (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([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])
index 254a983f25f8da612a211134b8a4c643eadc359c..cbe22d4f18c3114f1993829c5235b63fe8961942 100644 (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.  REM should be NULL.  */
 
-static char *
+char *
 format_pid (char *in, unsigned int pid)
 {
   unsigned int digits[32], *fill;
index ad1b50276c8ab317817d59c9a708da547b0825ac..3ce06c353115af99e36d024201451a58e1e6e17a 100644 (file)
@@ -180,6 +180,7 @@ extern int aarch64_set_regs (pid_t, USER_REGS_STRUCT *, bool);
 
 \f
 
+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 *,
index a7cbda54d68243973752f30dde0948c3fdc7826a..64dadc092c2d65dcaf53c3830686834ee59ff8fb 100644 (file)
@@ -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