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.
This commit is contained in:
parent
55f0b3e561
commit
c37b50ad41
3 changed files with 150 additions and 2 deletions
|
@ -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],
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
135
exec/trace.c
135
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, ®s, 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, ®s, true))
|
||||
return;
|
||||
#else /* !__aarch64__ */
|
||||
if (ptrace (PTRACE_SETREGS, tracee->pid, NULL, ®s))
|
||||
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. */
|
||||
|
|
Loading…
Add table
Reference in a new issue