diff options
| author | Po Lu | 2024-01-27 10:36:30 +0800 |
|---|---|---|
| committer | Po Lu | 2024-01-27 10:36:30 +0800 |
| commit | c37b50ad417c6cb340f54ffe218f5d889345451a (patch) | |
| tree | fc6b825b4e3489621d0d7738eb739fa74e56d059 /exec | |
| parent | 55f0b3e561034a1ad4235770d1c0685439a64fe5 (diff) | |
| download | emacs-c37b50ad417c6cb340f54ffe218f5d889345451a.tar.gz emacs-c37b50ad417c6cb340f54ffe218f5d889345451a.zip | |
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.ac | 13 | ||||
| -rw-r--r-- | exec/exec.h | 4 | ||||
| -rw-r--r-- | exec/trace.c | 135 |
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.]) | |||
| 131 | AH_TEMPLATE([CLONE3_SYSCALL], [Define to number of the `clone3' system call.]) | 131 | AH_TEMPLATE([CLONE3_SYSCALL], [Define to number of the `clone3' system call.]) |
| 132 | AH_TEMPLATE([READLINK_SYSCALL], [Define to number of the `readlink' system call.]) | 132 | AH_TEMPLATE([READLINK_SYSCALL], [Define to number of the `readlink' system call.]) |
| 133 | AH_TEMPLATE([READLINKAT_SYSCALL], [Define to number of the `readlinkat' system call.]) | 133 | AH_TEMPLATE([READLINKAT_SYSCALL], [Define to number of the `readlinkat' system call.]) |
| 134 | AH_TEMPLATE([OPEN_SYSCALL], [Define to number of the `open' system call.]) | ||
| 135 | AH_TEMPLATE([OPENAT_SYSCALL], [Define to number of the `openat' system call.]) | ||
| 134 | AH_TEMPLATE([REENTRANT], [Define to 1 if the library is used within a signal handler.]) | 136 | AH_TEMPLATE([REENTRANT], [Define to 1 if the library is used within a signal handler.]) |
| 135 | 137 | ||
| 136 | AC_CANONICAL_HOST | 138 | AC_CANONICAL_HOST |
| @@ -257,6 +259,8 @@ AS_CASE([$host], [x86_64-*linux*], | |||
| 257 | AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) | 259 | AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) |
| 258 | AC_DEFINE([READLINK_SYSCALL], [__NR_readlink]) | 260 | AC_DEFINE([READLINK_SYSCALL], [__NR_readlink]) |
| 259 | AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat]) | 261 | AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat]) |
| 262 | AC_DEFINE([OPEN_SYSCALL], [__NR_open]) | ||
| 263 | AC_DEFINE([OPENAT_SYSCALL], [__NR_openat]) | ||
| 260 | exec_CHECK_LINUX_CLONE3 | 264 | exec_CHECK_LINUX_CLONE3 |
| 261 | # Make sure the loader doesn't conflict with other position | 265 | # Make sure the loader doesn't conflict with other position |
| 262 | # dependent code. | 266 | # dependent code. |
| @@ -285,6 +289,8 @@ AS_CASE([$host], [x86_64-*linux*], | |||
| 285 | AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) | 289 | AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) |
| 286 | AC_DEFINE([READLINK_SYSCALL], [__NR_readlink]) | 290 | AC_DEFINE([READLINK_SYSCALL], [__NR_readlink]) |
| 287 | AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat]) | 291 | AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat]) |
| 292 | AC_DEFINE([OPEN_SYSCALL], [__NR_open]) | ||
| 293 | AC_DEFINE([OPENAT_SYSCALL], [__NR_openat]) | ||
| 288 | exec_CHECK_LINUX_CLONE3 | 294 | exec_CHECK_LINUX_CLONE3 |
| 289 | # Make sure the loader doesn't conflict with other position | 295 | # Make sure the loader doesn't conflict with other position |
| 290 | # dependent code. | 296 | # dependent code. |
| @@ -312,8 +318,9 @@ AS_CASE([$host], [x86_64-*linux*], | |||
| 312 | AC_DEFINE([INTERPRETER_BASE], [0x3f00000000]) | 318 | AC_DEFINE([INTERPRETER_BASE], [0x3f00000000]) |
| 313 | AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) | 319 | AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) |
| 314 | AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) | 320 | AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) |
| 315 | # Note that aarch64 has no `readlink'. | 321 | # Note that aarch64 has neither `readlink' nor `open'. |
| 316 | AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat]) | 322 | AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat]) |
| 323 | AC_DEFINE([OPENAT_SYSCALL], [__NR_openat]) | ||
| 317 | exec_CHECK_LINUX_CLONE3 | 324 | exec_CHECK_LINUX_CLONE3 |
| 318 | # Make sure the loader doesn't conflict with other position | 325 | # Make sure the loader doesn't conflict with other position |
| 319 | # dependent code. ARM places rather significant restrictions on | 326 | # dependent code. ARM places rather significant restrictions on |
| @@ -343,6 +350,8 @@ AS_CASE([$host], [x86_64-*linux*], | |||
| 343 | AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) | 350 | AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) |
| 344 | AC_DEFINE([READLINK_SYSCALL], [__NR_readlink]) | 351 | AC_DEFINE([READLINK_SYSCALL], [__NR_readlink]) |
| 345 | AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat]) | 352 | AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat]) |
| 353 | AC_DEFINE([OPEN_SYSCALL], [__NR_open]) | ||
| 354 | AC_DEFINE([OPENAT_SYSCALL], [__NR_openat]) | ||
| 346 | exec_CHECK_LINUX_CLONE3 | 355 | exec_CHECK_LINUX_CLONE3 |
| 347 | LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000" | 356 | LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000" |
| 348 | exec_loader=loader-armeabi.s], | 357 | exec_loader=loader-armeabi.s], |
| @@ -365,6 +374,8 @@ AS_CASE([$host], [x86_64-*linux*], | |||
| 365 | AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) | 374 | AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) |
| 366 | AC_DEFINE([READLINK_SYSCALL], [__NR_readlink]) | 375 | AC_DEFINE([READLINK_SYSCALL], [__NR_readlink]) |
| 367 | AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat]) | 376 | AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat]) |
| 377 | AC_DEFINE([OPEN_SYSCALL], [__NR_open]) | ||
| 378 | AC_DEFINE([OPENAT_SYSCALL], [__NR_openat]) | ||
| 368 | exec_CHECK_LINUX_CLONE3 | 379 | exec_CHECK_LINUX_CLONE3 |
| 369 | LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000" | 380 | LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000" |
| 370 | exec_loader=loader-armeabi.s], | 381 | 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 | |||
| 148 | /* The next process being traced. */ | 148 | /* The next process being traced. */ |
| 149 | struct exec_tracee *next; | 149 | struct exec_tracee *next; |
| 150 | 150 | ||
| 151 | /* Address of any stack pointer to restore after system call | ||
| 152 | completion. */ | ||
| 153 | USER_WORD sp; | ||
| 154 | |||
| 151 | /* The thread ID of this process. */ | 155 | /* The thread ID of this process. */ |
| 152 | pid_t pid; | 156 | pid_t pid; |
| 153 | 157 | ||
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, | |||
| 961 | return 0; | 961 | return 0; |
| 962 | 962 | ||
| 963 | /* Copy over tracee->exec_file. Truncate it to PATH_MAX, length, or | 963 | /* Copy over tracee->exec_file. Truncate it to PATH_MAX, length, or |
| 964 | size, whichever is less. */ | 964 | size, whichever is smaller. */ |
| 965 | 965 | ||
| 966 | length = strlen (tracee->exec_file); | 966 | length = strlen (tracee->exec_file); |
| 967 | length = MIN (size, MIN (PATH_MAX, length)); | 967 | length = MIN (size, MIN (PATH_MAX, length)); |
| @@ -979,6 +979,98 @@ handle_readlinkat (USER_WORD callno, USER_REGS_STRUCT *regs, | |||
| 979 | #endif /* REENTRANT */ | 979 | #endif /* REENTRANT */ |
| 980 | } | 980 | } |
| 981 | 981 | ||
| 982 | /* Handle an `open' or `openat' system call. | ||
| 983 | |||
| 984 | CALLNO is the system call number, and REGS are the current user | ||
| 985 | registers of the TRACEE. | ||
| 986 | |||
| 987 | If the file name specified in such system call is `/proc/self/exe', | ||
| 988 | replace the file name with the executable loaded into the process | ||
| 989 | issuing this system call. | ||
| 990 | |||
| 991 | Value is 0 upon success and 1 upon failure. */ | ||
| 992 | |||
| 993 | static int | ||
| 994 | handle_openat (USER_WORD callno, USER_REGS_STRUCT *regs, | ||
| 995 | struct exec_tracee *tracee, USER_WORD *result) | ||
| 996 | { | ||
| 997 | #ifdef REENTRANT | ||
| 998 | /* readlinkat cannot be handled specially when the library is built | ||
| 999 | to be reentrant, as the file name information cannot be | ||
| 1000 | recorded. */ | ||
| 1001 | return 0; | ||
| 1002 | #else /* !REENTRANT */ | ||
| 1003 | char buffer[PATH_MAX + 1]; | ||
| 1004 | USER_WORD address; | ||
| 1005 | size_t length; | ||
| 1006 | USER_REGS_STRUCT original; | ||
| 1007 | |||
| 1008 | /* Read the file name. */ | ||
| 1009 | |||
| 1010 | #ifdef OPEN_SYSCALL | ||
| 1011 | if (callno == OPEN_SYSCALL) | ||
| 1012 | address = regs->SYSCALL_ARG_REG; | ||
| 1013 | else | ||
| 1014 | #endif /* OPEN_SYSCALL */ | ||
| 1015 | address = regs->SYSCALL_ARG1_REG; | ||
| 1016 | |||
| 1017 | /* Read the file name into the buffer and verify that it is NULL | ||
| 1018 | terminated. */ | ||
| 1019 | read_memory (tracee, buffer, PATH_MAX, address); | ||
| 1020 | |||
| 1021 | if (!memchr (buffer, '\0', PATH_MAX)) | ||
| 1022 | { | ||
| 1023 | errno = ENAMETOOLONG; | ||
| 1024 | return 1; | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | /* Now check if the caller is looking for /proc/self/exe. | ||
| 1028 | |||
| 1029 | dirfd can be ignored, as for now only absolute file names are | ||
| 1030 | handled. FIXME. */ | ||
| 1031 | |||
| 1032 | if (strcmp (buffer, "/proc/self/exe") || !tracee->exec_file) | ||
| 1033 | return 0; | ||
| 1034 | |||
| 1035 | /* Copy over tracee->exec_file. This doesn't correctly handle the | ||
| 1036 | scenario where tracee->exec_file is longer than PATH_MAX, but | ||
| 1037 | that has yet to be encountered in practice. */ | ||
| 1038 | |||
| 1039 | original = *regs; | ||
| 1040 | length = strlen (tracee->exec_file); | ||
| 1041 | address = user_alloca (tracee, &original, regs, length + 1); | ||
| 1042 | |||
| 1043 | if (!address | ||
| 1044 | || user_copy (tracee, (unsigned char *) tracee->exec_file, | ||
| 1045 | address, length)) | ||
| 1046 | goto fail; | ||
| 1047 | |||
| 1048 | /* Replace the file name buffer with ADDRESS. */ | ||
| 1049 | |||
| 1050 | #ifdef OPEN_SYSCALL | ||
| 1051 | if (callno == OPEN_SYSCALL) | ||
| 1052 | regs->SYSCALL_ARG_REG = address; | ||
| 1053 | else | ||
| 1054 | #endif /* OPEN_SYSCALL */ | ||
| 1055 | regs->SYSCALL_ARG1_REG = address; | ||
| 1056 | |||
| 1057 | #ifdef __aarch64__ | ||
| 1058 | if (aarch64_set_regs (tracee->pid, regs, false)) | ||
| 1059 | goto fail; | ||
| 1060 | #else /* !__aarch64__ */ | ||
| 1061 | if (ptrace (PTRACE_SETREGS, tracee->pid, NULL, regs)) | ||
| 1062 | goto fail; | ||
| 1063 | #endif /* __aarch64__ */ | ||
| 1064 | |||
| 1065 | /* Resume the system call. */ | ||
| 1066 | return 0; | ||
| 1067 | |||
| 1068 | fail: | ||
| 1069 | errno = EIO; | ||
| 1070 | return 1; | ||
| 1071 | #endif /* REENTRANT */ | ||
| 1072 | } | ||
| 1073 | |||
| 982 | /* Process the system call at which TRACEE is stopped. If the system | 1074 | /* Process the system call at which TRACEE is stopped. If the system |
| 983 | call is not known or not exec, send TRACEE on its way. Otherwise, | 1075 | call is not known or not exec, send TRACEE on its way. Otherwise, |
| 984 | rewrite it to load the loader and perform an appropriate action. */ | 1076 | rewrite it to load the loader and perform an appropriate action. */ |
| @@ -1056,9 +1148,50 @@ process_system_call (struct exec_tracee *tracee) | |||
| 1056 | goto emulate_syscall; | 1148 | goto emulate_syscall; |
| 1057 | } | 1149 | } |
| 1058 | 1150 | ||
| 1151 | goto continue_syscall; | ||
| 1152 | |||
| 1153 | #ifdef OPEN_SYSCALL | ||
| 1154 | case OPEN_SYSCALL: | ||
| 1155 | #endif /* OPEN_SYSCALL */ | ||
| 1156 | case OPENAT_SYSCALL: | ||
| 1157 | |||
| 1158 | /* This system call is already in progress if | ||
| 1159 | TRACEE->waiting_for_syscall is true. */ | ||
| 1160 | |||
| 1161 | if (!tracee->waiting_for_syscall) | ||
| 1162 | { | ||
| 1163 | /* Handle this open system call. */ | ||
| 1164 | rc = handle_openat (callno, ®s, tracee, &result); | ||
| 1165 | |||
| 1166 | /* rc means the same as in `handle_exec', except that `open' | ||
| 1167 | is never emulated. */ | ||
| 1168 | |||
| 1169 | if (rc == 1) | ||
| 1170 | goto report_syscall_error; | ||
| 1171 | |||
| 1172 | /* The stack pointer must be restored after it was modified | ||
| 1173 | by `user_alloca'; record sp in TRACEE, which will be | ||
| 1174 | restored after this system call completes. */ | ||
| 1175 | tracee->sp = sp; | ||
| 1176 | } | ||
| 1177 | else | ||
| 1178 | { | ||
| 1179 | /* Restore that stack pointer. */ | ||
| 1180 | regs.STACK_POINTER = tracee->sp; | ||
| 1181 | |||
| 1182 | #ifdef __aarch64__ | ||
| 1183 | if (aarch64_set_regs (tracee->pid, ®s, true)) | ||
| 1184 | return; | ||
| 1185 | #else /* !__aarch64__ */ | ||
| 1186 | if (ptrace (PTRACE_SETREGS, tracee->pid, NULL, ®s)) | ||
| 1187 | return; | ||
| 1188 | #endif /* __aarch64__ */ | ||
| 1189 | } | ||
| 1190 | |||
| 1059 | /* Fallthrough. */ | 1191 | /* Fallthrough. */ |
| 1060 | 1192 | ||
| 1061 | default: | 1193 | default: |
| 1194 | continue_syscall: | ||
| 1062 | /* Don't wait for the system call to finish; instead, the system | 1195 | /* Don't wait for the system call to finish; instead, the system |
| 1063 | will DTRT upon the next call to PTRACE_SYSCALL after the | 1196 | will DTRT upon the next call to PTRACE_SYSCALL after the |
| 1064 | syscall-trap signal is delivered. */ | 1197 | syscall-trap signal is delivered. */ |