mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-06 06:20:55 -08:00
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.
This commit is contained in:
parent
db5c8bda63
commit
30bc867aec
4 changed files with 121 additions and 11 deletions
|
|
@ -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([STACK_POINTER], [Define to register holding the stack pointer.])
|
||||||
AH_TEMPLATE([EXEC_SYSCALL], [Define to number of the `exec' system call.])
|
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_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([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([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.])
|
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([STACK_POINTER], [rsp])
|
||||||
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
|
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
|
||||||
AC_DEFINE([USER_WORD], [uintptr_t])
|
AC_DEFINE([USER_WORD], [uintptr_t])
|
||||||
|
AC_DEFINE([USER_SWORD], [intptr_t])
|
||||||
AC_DEFINE([EXEC_64], [1])
|
AC_DEFINE([EXEC_64], [1])
|
||||||
AC_DEFINE([ABI_RED_ZONE], [128])
|
AC_DEFINE([ABI_RED_ZONE], [128])
|
||||||
AC_DEFINE([EXECUTABLE_BASE], [0x555555554000])
|
AC_DEFINE([EXECUTABLE_BASE], [0x555555554000])
|
||||||
|
|
@ -283,6 +285,7 @@ AS_CASE([$host], [x86_64-*linux*],
|
||||||
AC_DEFINE([STACK_POINTER], [esp])
|
AC_DEFINE([STACK_POINTER], [esp])
|
||||||
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
|
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
|
||||||
AC_DEFINE([USER_WORD], [uintptr_t])
|
AC_DEFINE([USER_WORD], [uintptr_t])
|
||||||
|
AC_DEFINE([USER_SWORD], [intptr_t])
|
||||||
AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
|
AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
|
||||||
AC_DEFINE([INTERPRETER_BASE], [0xaf000000])
|
AC_DEFINE([INTERPRETER_BASE], [0xaf000000])
|
||||||
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
|
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
|
||||||
|
|
@ -313,6 +316,7 @@ AS_CASE([$host], [x86_64-*linux*],
|
||||||
AC_DEFINE([STACK_POINTER], [sp])
|
AC_DEFINE([STACK_POINTER], [sp])
|
||||||
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
|
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
|
||||||
AC_DEFINE([USER_WORD], [uintptr_t])
|
AC_DEFINE([USER_WORD], [uintptr_t])
|
||||||
|
AC_DEFINE([USER_SWORD], [intptr_t])
|
||||||
AC_DEFINE([EXEC_64], [1])
|
AC_DEFINE([EXEC_64], [1])
|
||||||
AC_DEFINE([EXECUTABLE_BASE], [0x3000000000])
|
AC_DEFINE([EXECUTABLE_BASE], [0x3000000000])
|
||||||
AC_DEFINE([INTERPRETER_BASE], [0x3f00000000])
|
AC_DEFINE([INTERPRETER_BASE], [0x3f00000000])
|
||||||
|
|
@ -344,6 +348,7 @@ AS_CASE([$host], [x86_64-*linux*],
|
||||||
AC_DEFINE([STACK_POINTER], [[uregs[13]]])
|
AC_DEFINE([STACK_POINTER], [[uregs[13]]])
|
||||||
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
|
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
|
||||||
AC_DEFINE([USER_WORD], [uintptr_t])
|
AC_DEFINE([USER_WORD], [uintptr_t])
|
||||||
|
AC_DEFINE([USER_SWORD], [intptr_t])
|
||||||
AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
|
AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
|
||||||
AC_DEFINE([INTERPRETER_BASE], [0x1f000000])
|
AC_DEFINE([INTERPRETER_BASE], [0x1f000000])
|
||||||
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
|
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
|
||||||
|
|
@ -368,6 +373,7 @@ AS_CASE([$host], [x86_64-*linux*],
|
||||||
AC_DEFINE([STACK_POINTER], [[uregs[13]]])
|
AC_DEFINE([STACK_POINTER], [[uregs[13]]])
|
||||||
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
|
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
|
||||||
AC_DEFINE([USER_WORD], [uintptr_t])
|
AC_DEFINE([USER_WORD], [uintptr_t])
|
||||||
|
AC_DEFINE([USER_SWORD], [intptr_t])
|
||||||
AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
|
AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
|
||||||
AC_DEFINE([INTERPRETER_BASE], [0x1f000000])
|
AC_DEFINE([INTERPRETER_BASE], [0x1f000000])
|
||||||
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
|
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([STACK_POINTER], [[gregs[29]]]) # sp
|
||||||
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
|
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
|
||||||
AC_DEFINE([USER_WORD], [uintptr_t])
|
AC_DEFINE([USER_WORD], [uintptr_t])
|
||||||
|
AC_DEFINE([USER_SWORD], [intptr_t])
|
||||||
AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
|
AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
|
||||||
AC_DEFINE([INTERPRETER_BASE], [0x1f000000])
|
AC_DEFINE([INTERPRETER_BASE], [0x1f000000])
|
||||||
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
|
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([STACK_POINTER], [[gregs[29]]]) # sp
|
||||||
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
|
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
|
||||||
AC_DEFINE([USER_WORD], [uintptr_t])
|
AC_DEFINE([USER_WORD], [uintptr_t])
|
||||||
|
AC_DEFINE([USER_SWORD], [intptr_t])
|
||||||
AC_DEFINE([EXEC_64], [1])
|
AC_DEFINE([EXEC_64], [1])
|
||||||
AC_DEFINE([EXECUTABLE_BASE], [0x400000])
|
AC_DEFINE([EXECUTABLE_BASE], [0x400000])
|
||||||
AC_DEFINE([INTERPRETER_BASE], [0x3f00000000])
|
AC_DEFINE([INTERPRETER_BASE], [0x3f00000000])
|
||||||
|
|
|
||||||
|
|
@ -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 in *IN, and return a pointer to the byte after the
|
||||||
result. REM should be NULL. */
|
result. REM should be NULL. */
|
||||||
|
|
||||||
static char *
|
char *
|
||||||
format_pid (char *in, unsigned int pid)
|
format_pid (char *in, unsigned int pid)
|
||||||
{
|
{
|
||||||
unsigned int digits[32], *fill;
|
unsigned int digits[32], *fill;
|
||||||
|
|
|
||||||
|
|
@ -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 *,
|
extern USER_WORD user_alloca (struct exec_tracee *, USER_REGS_STRUCT *,
|
||||||
USER_REGS_STRUCT *, USER_WORD);
|
USER_REGS_STRUCT *, USER_WORD);
|
||||||
extern int user_copy (struct exec_tracee *, const unsigned char *,
|
extern int user_copy (struct exec_tracee *, const unsigned char *,
|
||||||
|
|
|
||||||
109
exec/trace.c
109
exec/trace.c
|
|
@ -31,6 +31,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include "exec.h"
|
#include "exec.h"
|
||||||
|
|
||||||
|
|
@ -894,6 +895,68 @@ handle_exec (struct exec_tracee *tracee, USER_REGS_STRUCT *regs)
|
||||||
return 3;
|
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.
|
/* Handle a `readlink' or `readlinkat' system call.
|
||||||
|
|
||||||
CALLNO is the system call number, and REGS are the current user
|
CALLNO is the system call number, and REGS are the current user
|
||||||
|
|
@ -924,12 +987,15 @@ handle_readlinkat (USER_WORD callno, USER_REGS_STRUCT *regs,
|
||||||
char buffer[PATH_MAX + 1];
|
char buffer[PATH_MAX + 1];
|
||||||
USER_WORD address, return_buffer, size;
|
USER_WORD address, return_buffer, size;
|
||||||
size_t length;
|
size_t length;
|
||||||
|
char proc_pid_exe[sizeof "/proc//exe" + 24], *p;
|
||||||
|
int dirfd;
|
||||||
|
|
||||||
/* Read the file name. */
|
/* Read the file name. */
|
||||||
|
|
||||||
#ifdef READLINK_SYSCALL
|
#ifdef READLINK_SYSCALL
|
||||||
if (callno == READLINK_SYSCALL)
|
if (callno == READLINK_SYSCALL)
|
||||||
{
|
{
|
||||||
|
dirfd = AT_FDCWD;
|
||||||
address = regs->SYSCALL_ARG_REG;
|
address = regs->SYSCALL_ARG_REG;
|
||||||
return_buffer = regs->SYSCALL_ARG1_REG;
|
return_buffer = regs->SYSCALL_ARG1_REG;
|
||||||
size = regs->SYSCALL_ARG2_REG;
|
size = regs->SYSCALL_ARG2_REG;
|
||||||
|
|
@ -937,6 +1003,7 @@ handle_readlinkat (USER_WORD callno, USER_REGS_STRUCT *regs,
|
||||||
else
|
else
|
||||||
#endif /* READLINK_SYSCALL */
|
#endif /* READLINK_SYSCALL */
|
||||||
{
|
{
|
||||||
|
dirfd = (USER_SWORD) regs->SYSCALL_ARG_REG;
|
||||||
address = regs->SYSCALL_ARG1_REG;
|
address = regs->SYSCALL_ARG1_REG;
|
||||||
return_buffer = regs->SYSCALL_ARG2_REG;
|
return_buffer = regs->SYSCALL_ARG2_REG;
|
||||||
size = regs->SYSCALL_ARG3_REG;
|
size = regs->SYSCALL_ARG3_REG;
|
||||||
|
|
@ -952,12 +1019,25 @@ handle_readlinkat (USER_WORD callno, USER_REGS_STRUCT *regs,
|
||||||
return 1;
|
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
|
dirfd can be ignored, as for now only absolute file names are
|
||||||
handled. FIXME. */
|
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;
|
return 0;
|
||||||
|
|
||||||
/* Copy over tracee->exec_file. Truncate it to PATH_MAX, length, or
|
/* 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;
|
USER_WORD address;
|
||||||
size_t length;
|
size_t length;
|
||||||
USER_REGS_STRUCT original;
|
USER_REGS_STRUCT original;
|
||||||
|
char proc_pid_exe[sizeof "/proc//exe" + 24], *p;
|
||||||
|
int dirfd;
|
||||||
|
|
||||||
/* Read the file name. */
|
/* Read the file name. */
|
||||||
|
|
||||||
#ifdef OPEN_SYSCALL
|
#ifdef OPEN_SYSCALL
|
||||||
if (callno == OPEN_SYSCALL)
|
if (callno == OPEN_SYSCALL)
|
||||||
|
{
|
||||||
|
dirfd = AT_FDCWD;
|
||||||
address = regs->SYSCALL_ARG_REG;
|
address = regs->SYSCALL_ARG_REG;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
#endif /* OPEN_SYSCALL */
|
#endif /* OPEN_SYSCALL */
|
||||||
|
{
|
||||||
|
dirfd = (USER_SWORD) regs->SYSCALL_ARG_REG;
|
||||||
address = regs->SYSCALL_ARG1_REG;
|
address = regs->SYSCALL_ARG1_REG;
|
||||||
|
}
|
||||||
|
|
||||||
/* Read the file name into the buffer and verify that it is NULL
|
/* Read the file name into the buffer and verify that it is NULL
|
||||||
terminated. */
|
terminated. */
|
||||||
|
|
@ -1024,12 +1112,25 @@ handle_openat (USER_WORD callno, USER_REGS_STRUCT *regs,
|
||||||
return 1;
|
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
|
dirfd can be ignored, as for now only absolute file names are
|
||||||
handled. FIXME. */
|
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;
|
return 0;
|
||||||
|
|
||||||
/* Copy over tracee->exec_file. This doesn't correctly handle the
|
/* Copy over tracee->exec_file. This doesn't correctly handle the
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue