Skip to content

Commit

Permalink
Hook readlink(3) of /self/${PID}/exe
Browse files Browse the repository at this point in the history
  • Loading branch information
fornwall committed Oct 7, 2024
1 parent df8a21c commit fe5e044
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 4 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ on-device-tests-internal: libtermux-exec.so tests/fexecve tests/popen tests/syst
@LD_PRELOAD=${CURDIR}/libtermux-exec.so ./run-tests.sh

format:
$(CLANG_FORMAT) -i $(C_SOURCE)
$(CLANG_FORMAT) -i $(C_SOURCE) tests/*.c

check:
$(CLANG_FORMAT) --dry-run $(C_SOURCE)
Expand Down
4 changes: 2 additions & 2 deletions src/termux-exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -351,8 +351,8 @@ __attribute__((visibility("default"))) int execve(const char *executable_path, c
char const *executable_path_resolved = realpath(executable_path, executable_path_resolved_buffer);
char const *path_to_use = executable_path_resolved ? executable_path_resolved : executable_path;
bool wrap_in_linker = (strstr(path_to_use, TERMUX_BASE_DIR) == path_to_use)
// /system/bin/sh is fine, it only uses libc++, libc, and libdl.
|| (strcmp(path_to_use, "/system/bin/sh") == 0);
// /system/bin/sh is fine, it only uses libc++, libc, and libdl.
|| (strcmp(path_to_use, "/system/bin/sh") == 0);

// Avoid interfering with Android /system software by removing
// LD_PRELOAD and LD_LIBRARY_PATH from env if executing something
Expand Down
37 changes: 37 additions & 0 deletions src/termux-readlink.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
Expand All @@ -20,6 +21,42 @@ __attribute__((visibility("default"))) ssize_t readlink(const char *restrict pat
memcpy(buf, termux_self_exe, bytes_to_copy);
return bytes_to_copy;
}
} else if (strncmp(pathname, "/proc/", strlen("/proc/")) == 0) {
// See if /proc/$PID/exe is being resolved.
size_t path_len = strlen(pathname);
if (pathname[path_len - 4] == '/' && pathname[path_len - 3] == 'e' && pathname[path_len - 2] == 'x' &&
pathname[path_len - 1] == 'e' && path_len < 30) {
char environ_path_buf[PATH_MAX];
memcpy(environ_path_buf, pathname, path_len - 3);
memcpy(environ_path_buf + (path_len - 3), "environ", sizeof("environ"));
int environ_fd = TEMP_FAILURE_RETRY(open(environ_path_buf, O_RDONLY));
if (environ_fd > 0) {
char environ_buf[16 * 4096];
ssize_t environ_size = TEMP_FAILURE_RETRY(read(environ_fd, environ_buf, sizeof(environ_buf)));
close(environ_fd);
if (environ_size > 0) {
size_t start_offset = 0;
for (size_t i = 0; i < (size_t)environ_size; i++) {
if (environ_buf[i] == 0 && start_offset != i) {
if (strncmp(&environ_buf[start_offset],
"TERMUX_EXEC__PROC_SELF_EXE=", strlen("TERMUX_EXEC__PROC_SELF_EXE=")) == 0) {
char *termux_self_exe = &environ_buf[start_offset + strlen("TERMUX_EXEC__PROC_SELF_EXE=")];
char resolved_path_buf[PATH_MAX];
char *resolved_path = realpath(termux_self_exe, resolved_path_buf);
if (resolved_path) {
termux_self_exe = resolved_path_buf;
}
size_t termux_self_exe_len = strlen(termux_self_exe);
size_t bytes_to_copy = (termux_self_exe_len < bufsiz) ? termux_self_exe_len : bufsiz;
memcpy(buf, termux_self_exe, bytes_to_copy);
return bytes_to_copy;
}
start_offset = i + 1;
}
}
}
}
}
}

return syscall(SYS_readlinkat, AT_FDCWD, pathname, buf, bufsiz);
Expand Down
24 changes: 23 additions & 1 deletion tests/readlink-proc-self-exe.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#define _DEFAULT_SOURCE
#include <linux/limits.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main() {
Expand All @@ -11,8 +12,29 @@ int main() {
perror("readlink()");
return 1;
}

buf[res] = 0;
printf("%s\n", buf);

// Validate that reading /proc/$PID/exe gives the same result as /proc/self/exe
char *proc_pid_exe_path;
asprintf(&proc_pid_exe_path, "/proc/%ld/exe", (long)getpid());
char pid_buf[PATH_MAX + 1];
int fd = open(proc_pid_exe_path, O_RDONLY);
if (fd <= 0) {
perror("open()");
return 1;
}
ssize_t pid_res = readlink(proc_pid_exe_path, pid_buf, PATH_MAX);
if (pid_res <= 0) {
perror("readlink()");
return 1;
}
pid_buf[pid_res] = 0;
if (strcmp(buf, pid_buf) != 0) {
fprintf(stderr, "Mismatch - readlink(/proc/self/exe)='%s', readlink(/proc/$PID/exe)='%s'\n", buf, pid_buf);
return 1;
}

return 0;
}

0 comments on commit fe5e044

Please sign in to comment.