emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 30bc867aecc: Improve /proc/self/exe substitution on Android


From: Po Lu
Subject: master 30bc867aecc: Improve /proc/self/exe substitution on Android
Date: Thu, 14 Mar 2024 01:50:41 -0400 (EDT)

branch: master
commit 30bc867aecc59265b6e315acf459f8d79c423bca
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>

    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.
---
 exec/configure.ac |   8 ++++
 exec/exec.c       |   2 +-
 exec/exec.h       |   1 +
 exec/trace.c      | 121 +++++++++++++++++++++++++++++++++++++++++++++++++-----
 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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]