summaryrefslogtreecommitdiff
path: root/exec
diff options
context:
space:
mode:
authorPo Lu <luangruo@yahoo.com>2024-01-27 10:36:30 +0800
committerPo Lu <luangruo@yahoo.com>2024-01-27 10:36:30 +0800
commitc37b50ad417c6cb340f54ffe218f5d889345451a (patch)
treefc6b825b4e3489621d0d7738eb739fa74e56d059 /exec
parent55f0b3e561034a1ad4235770d1c0685439a64fe5 (diff)
downloademacs-c37b50ad417c6cb340f54ffe218f5d889345451a.tar.gz
Intercept calls to `openat' under Android
* 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.
Diffstat (limited to 'exec')
-rw-r--r--exec/configure.ac13
-rw-r--r--exec/exec.h4
-rw-r--r--exec/trace.c135
3 files changed, 150 insertions, 2 deletions
diff --git a/exec/configure.ac b/exec/configure.ac
index 9008c84f6a6..d70dbea3477 100644
--- a/exec/configure.ac
+++ b/exec/configure.ac
@@ -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],
diff --git a/exec/exec.h b/exec/exec.h
index bed5edc9bab..ad1b50276c8 100644
--- a/exec/exec.h
+++ b/exec/exec.h
@@ -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;
diff --git a/exec/trace.c b/exec/trace.c
index 8e190c94f79..a7cbda54d68 100644
--- a/exec/trace.c
+++ b/exec/trace.c
@@ -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. */