]> git.eshelyaron.com Git - emacs.git/commitdiff
Intercept calls to `openat' under Android
authorPo Lu <luangruo@yahoo.com>
Sat, 27 Jan 2024 02:36:30 +0000 (10:36 +0800)
committerEshel Yaron <me@eshelyaron.com>
Wed, 31 Jan 2024 20:07:09 +0000 (21:07 +0100)
* exec/configure.ac (OPEN_SYSCALL, OPENAT_SYSCALL): Define new
macros.

* exec/exec.h (struct exec_tracee): New field `sp'.

* exec/trace.c (handle_openat): New function.
(process_system_call): If handle_openat executes successfully,
save the unmodified stack pointer within the tracee structure to
be restored once the system call completes.

(cherry picked from commit c37b50ad417c6cb340f54ffe218f5d889345451a)

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

index 9008c84f6a69c8a02734d1175c736206b994eaf7..d70dbea34771559ebba961a3986a7d63749b5ffb 100644 (file)
@@ -131,6 +131,8 @@ AH_TEMPLATE([CLONE_SYSCALL], [Define to number of the `clone' system call.])
 AH_TEMPLATE([CLONE3_SYSCALL], [Define to number of the `clone3' system call.])
 AH_TEMPLATE([READLINK_SYSCALL], [Define to number of the `readlink' system call.])
 AH_TEMPLATE([READLINKAT_SYSCALL], [Define to number of the `readlinkat' system call.])
+AH_TEMPLATE([OPEN_SYSCALL], [Define to number of the `open' system call.])
+AH_TEMPLATE([OPENAT_SYSCALL], [Define to number of the `openat' system call.])
 AH_TEMPLATE([REENTRANT], [Define to 1 if the library is used within a signal handler.])
 
 AC_CANONICAL_HOST
@@ -257,6 +259,8 @@ AS_CASE([$host], [x86_64-*linux*],
      AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
      AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
      AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
+     AC_DEFINE([OPEN_SYSCALL], [__NR_open])
+     AC_DEFINE([OPENAT_SYSCALL], [__NR_openat])
      exec_CHECK_LINUX_CLONE3
      # Make sure the loader doesn't conflict with other position
      # dependent code.
@@ -285,6 +289,8 @@ AS_CASE([$host], [x86_64-*linux*],
      AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
      AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
      AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
+     AC_DEFINE([OPEN_SYSCALL], [__NR_open])
+     AC_DEFINE([OPENAT_SYSCALL], [__NR_openat])
      exec_CHECK_LINUX_CLONE3
      # Make sure the loader doesn't conflict with other position
      # dependent code.
@@ -312,8 +318,9 @@ AS_CASE([$host], [x86_64-*linux*],
      AC_DEFINE([INTERPRETER_BASE], [0x3f00000000])
      AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
      AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
-     # Note that aarch64 has no `readlink'.
+     # Note that aarch64 has neither `readlink' nor `open'.
      AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
+     AC_DEFINE([OPENAT_SYSCALL], [__NR_openat])
      exec_CHECK_LINUX_CLONE3
      # Make sure the loader doesn't conflict with other position
      # dependent code.  ARM places rather significant restrictions on
@@ -343,6 +350,8 @@ AS_CASE([$host], [x86_64-*linux*],
      AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
      AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
      AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
+     AC_DEFINE([OPEN_SYSCALL], [__NR_open])
+     AC_DEFINE([OPENAT_SYSCALL], [__NR_openat])
      exec_CHECK_LINUX_CLONE3
      LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000"
      exec_loader=loader-armeabi.s],
@@ -365,6 +374,8 @@ AS_CASE([$host], [x86_64-*linux*],
        AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
        AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
        AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
+       AC_DEFINE([OPEN_SYSCALL], [__NR_open])
+       AC_DEFINE([OPENAT_SYSCALL], [__NR_openat])
        exec_CHECK_LINUX_CLONE3
        LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000"
        exec_loader=loader-armeabi.s],
index bed5edc9bab3503cb9a2f9a9a8f1e0a0bb544af1..ad1b50276c8ab317817d59c9a708da547b0825ac 100644 (file)
@@ -148,6 +148,10 @@ struct exec_tracee
   /* The next process being traced.  */
   struct exec_tracee *next;
 
+  /* Address of any stack pointer to restore after system call
+     completion.  */
+  USER_WORD sp;
+
   /* The thread ID of this process.  */
   pid_t pid;
 
index 8e190c94f79fcf615c92a7dfcd07af75cfe4f38b..a7cbda54d68243973752f30dde0948c3fdc7826a 100644 (file)
@@ -961,7 +961,7 @@ handle_readlinkat (USER_WORD callno, USER_REGS_STRUCT *regs,
     return 0;
 
   /* Copy over tracee->exec_file.  Truncate it to PATH_MAX, length, or
-     size, whichever is less.  */
+     size, whichever is smaller.  */
 
   length = strlen (tracee->exec_file);
   length = MIN (size, MIN (PATH_MAX, length));
@@ -979,6 +979,98 @@ handle_readlinkat (USER_WORD callno, USER_REGS_STRUCT *regs,
 #endif /* REENTRANT */
 }
 
+/* Handle an `open' or `openat' system call.
+
+   CALLNO is the system call number, and REGS are the current user
+   registers of the TRACEE.
+
+   If the file name specified in such system call is `/proc/self/exe',
+   replace the file name with the executable loaded into the process
+   issuing this system call.
+
+   Value is 0 upon success and 1 upon failure.  */
+
+static int
+handle_openat (USER_WORD callno, USER_REGS_STRUCT *regs,
+              struct exec_tracee *tracee, USER_WORD *result)
+{
+#ifdef REENTRANT
+  /* readlinkat cannot be handled specially when the library is built
+     to be reentrant, as the file name information cannot be
+     recorded.  */
+  return 0;
+#else /* !REENTRANT */
+  char buffer[PATH_MAX + 1];
+  USER_WORD address;
+  size_t length;
+  USER_REGS_STRUCT original;
+
+  /* Read the file name.  */
+
+#ifdef OPEN_SYSCALL
+  if (callno == OPEN_SYSCALL)
+    address = regs->SYSCALL_ARG_REG;
+  else
+#endif /* OPEN_SYSCALL */
+    address = regs->SYSCALL_ARG1_REG;
+
+  /* Read the file name into the buffer and verify that it is NULL
+     terminated.  */
+  read_memory (tracee, buffer, PATH_MAX, address);
+
+  if (!memchr (buffer, '\0', PATH_MAX))
+    {
+      errno = ENAMETOOLONG;
+      return 1;
+    }
+
+  /* Now check if the caller is looking for /proc/self/exe.
+
+     dirfd can be ignored, as for now only absolute file names are
+     handled.  FIXME.  */
+
+  if (strcmp (buffer, "/proc/self/exe") || !tracee->exec_file)
+    return 0;
+
+  /* Copy over tracee->exec_file.  This doesn't correctly handle the
+     scenario where tracee->exec_file is longer than PATH_MAX, but
+     that has yet to be encountered in practice.  */
+
+  original = *regs;
+  length   = strlen (tracee->exec_file);
+  address  = user_alloca (tracee, &original, regs, length + 1);
+
+  if (!address
+      || user_copy (tracee, (unsigned char *) tracee->exec_file,
+                   address, length))
+    goto fail;
+
+  /* Replace the file name buffer with ADDRESS.  */
+
+#ifdef OPEN_SYSCALL
+  if (callno == OPEN_SYSCALL)
+    regs->SYSCALL_ARG_REG = address;
+  else
+#endif /* OPEN_SYSCALL */
+    regs->SYSCALL_ARG1_REG = address;
+
+#ifdef __aarch64__
+  if (aarch64_set_regs (tracee->pid, regs, false))
+    goto fail;
+#else /* !__aarch64__ */
+  if (ptrace (PTRACE_SETREGS, tracee->pid, NULL, regs))
+    goto fail;
+#endif /* __aarch64__ */
+
+  /* Resume the system call.  */
+  return 0;
+
+ fail:
+  errno = EIO;
+  return 1;
+#endif /* REENTRANT */
+}
+
 /* Process the system call at which TRACEE is stopped.  If the system
    call is not known or not exec, send TRACEE on its way.  Otherwise,
    rewrite it to load the loader and perform an appropriate action.  */
@@ -1056,9 +1148,50 @@ process_system_call (struct exec_tracee *tracee)
            goto emulate_syscall;
        }
 
+      goto continue_syscall;
+
+#ifdef OPEN_SYSCALL
+    case OPEN_SYSCALL:
+#endif /* OPEN_SYSCALL */
+    case OPENAT_SYSCALL:
+
+      /* This system call is already in progress if
+        TRACEE->waiting_for_syscall is true.  */
+
+      if (!tracee->waiting_for_syscall)
+       {
+         /* Handle this open system call.  */
+         rc = handle_openat (callno, &regs, tracee, &result);
+
+         /* rc means the same as in `handle_exec', except that `open'
+            is never emulated.  */
+
+         if (rc == 1)
+           goto report_syscall_error;
+
+         /* The stack pointer must be restored after it was modified
+            by `user_alloca'; record sp in TRACEE, which will be
+            restored after this system call completes.  */
+         tracee->sp = sp;
+       }
+      else
+       {
+         /* Restore that stack pointer.  */
+         regs.STACK_POINTER = tracee->sp;
+
+#ifdef __aarch64__
+         if (aarch64_set_regs (tracee->pid, &regs, true))
+           return;
+#else /* !__aarch64__ */
+         if (ptrace (PTRACE_SETREGS, tracee->pid, NULL, &regs))
+           return;
+#endif /* __aarch64__ */
+       }
+
       /* Fallthrough.  */
 
     default:
+    continue_syscall:
       /* Don't wait for the system call to finish; instead, the system
         will DTRT upon the next call to PTRACE_SYSCALL after the
         syscall-trap signal is delivered.  */