Skip to content

Commit d7f8257

Browse files
committed
Hook readlink(3) of /self/${PID}/exe
1 parent df8a21c commit d7f8257

File tree

4 files changed

+63
-4
lines changed

4 files changed

+63
-4
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ on-device-tests-internal: libtermux-exec.so tests/fexecve tests/popen tests/syst
5050
@LD_PRELOAD=${CURDIR}/libtermux-exec.so ./run-tests.sh
5151

5252
format:
53-
$(CLANG_FORMAT) -i $(C_SOURCE)
53+
$(CLANG_FORMAT) -i $(C_SOURCE) tests/*.c
5454

5555
check:
5656
$(CLANG_FORMAT) --dry-run $(C_SOURCE)

src/termux-exec.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -351,8 +351,8 @@ __attribute__((visibility("default"))) int execve(const char *executable_path, c
351351
char const *executable_path_resolved = realpath(executable_path, executable_path_resolved_buffer);
352352
char const *path_to_use = executable_path_resolved ? executable_path_resolved : executable_path;
353353
bool wrap_in_linker = (strstr(path_to_use, TERMUX_BASE_DIR) == path_to_use)
354-
// /system/bin/sh is fine, it only uses libc++, libc, and libdl.
355-
|| (strcmp(path_to_use, "/system/bin/sh") == 0);
354+
// /system/bin/sh is fine, it only uses libc++, libc, and libdl.
355+
|| (strcmp(path_to_use, "/system/bin/sh") == 0);
356356

357357
// Avoid interfering with Android /system software by removing
358358
// LD_PRELOAD and LD_LIBRARY_PATH from env if executing something

src/termux-readlink.c

+37
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#define _GNU_SOURCE
2+
#include <errno.h>
23
#include <fcntl.h>
34
#include <stdlib.h>
45
#include <string.h>
@@ -20,6 +21,42 @@ __attribute__((visibility("default"))) ssize_t readlink(const char *restrict pat
2021
memcpy(buf, termux_self_exe, bytes_to_copy);
2122
return bytes_to_copy;
2223
}
24+
} else if (strncmp(pathname, "/proc/", strlen("/proc/")) == 0) {
25+
// See if /proc/$PID/exe is being resolved.
26+
size_t path_len = strlen(pathname);
27+
if (pathname[path_len - 4] == '/' && pathname[path_len - 3] == 'e' && pathname[path_len - 2] == 'x' &&
28+
pathname[path_len - 1] == 'e' && path_len < 30) {
29+
char environ_path_buf[PATH_MAX];
30+
memcpy(environ_path_buf, pathname, path_len - 3);
31+
memcpy(environ_path_buf + (path_len - 3), "environ", sizeof("environ"));
32+
int environ_fd = TEMP_FAILURE_RETRY(open(environ_path_buf, O_RDONLY));
33+
if (environ_fd > 0) {
34+
char environ_buf[16 * 4096];
35+
ssize_t environ_size = TEMP_FAILURE_RETRY(read(environ_fd, environ_buf, sizeof(environ_buf)));
36+
close(environ_fd);
37+
if (environ_size > 0) {
38+
size_t start_offset = 0;
39+
for (size_t i = 0; i < (size_t)environ_size; i++) {
40+
if (environ_buf[i] == 0 && start_offset != i) {
41+
if (strncmp(&environ_buf[start_offset],
42+
"TERMUX_EXEC__PROC_SELF_EXE=", strlen("TERMUX_EXEC__PROC_SELF_EXE=")) == 0) {
43+
char *termux_self_exe = &environ_buf[start_offset + strlen("TERMUX_EXEC__PROC_SELF_EXE=")];
44+
char resolved_path_buf[PATH_MAX];
45+
char *resolved_path = realpath(termux_self_exe, resolved_path_buf);
46+
if (resolved_path) {
47+
termux_self_exe = resolved_path_buf;
48+
}
49+
size_t termux_self_exe_len = strlen(termux_self_exe);
50+
size_t bytes_to_copy = (termux_self_exe_len < bufsiz) ? termux_self_exe_len : bufsiz;
51+
memcpy(buf, termux_self_exe, bytes_to_copy);
52+
return bytes_to_copy;
53+
}
54+
start_offset = i + 1;
55+
}
56+
}
57+
}
58+
}
59+
}
2360
}
2461

2562
return syscall(SYS_readlinkat, AT_FDCWD, pathname, buf, bufsiz);

tests/readlink-proc-self-exe.c

+23-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#define _DEFAULT_SOURCE
22
#include <linux/limits.h>
33
#include <stdio.h>
4+
#include <string.h>
45
#include <unistd.h>
56

67
int main() {
@@ -11,8 +12,29 @@ int main() {
1112
perror("readlink()");
1213
return 1;
1314
}
15+
1416
buf[res] = 0;
1517
printf("%s\n", buf);
18+
19+
// Validate that reading /proc/$PID/exe gives the same result as /proc/self/exe
20+
char *proc_pid_exe_path;
21+
asprintf(&proc_pid_exe_path, "/proc/%ld/exe", (long)getpid());
22+
char pid_buf[PATH_MAX + 1];
23+
int fd = open(proc_pid_exe_path, O_RDONLY);
24+
if (fd <= 0) {
25+
perror("open()");
26+
return 1;
27+
}
28+
ssize_t pid_res = readlink(proc_pid_exe_path, pid_buf, PATH_MAX);
29+
if (pid_res <= 0) {
30+
perror("readlink()");
31+
return 1;
32+
}
33+
pid_buf[pid_res] = 0;
34+
if (strcmp(buf, pid_buf) != 0) {
35+
fprintf(stderr, "Mismatch - readlink(/proc/self/exe)='%s', readlink(/proc/$PID/exe)='%s'\n", buf, pid_buf);
36+
return 1;
37+
}
38+
1639
return 0;
1740
}
18-

0 commit comments

Comments
 (0)