Skip to content

Commit 0449b6e

Browse files
committed
Add ptrace-based vsyscall emulation
1 parent 85efbd9 commit 0449b6e

File tree

5 files changed

+189
-0
lines changed

5 files changed

+189
-0
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ matrix:
2323
- env: PLATFORM="x86_64"
2424

2525
script:
26+
- make -C docker/vsyscall_emu
2627
- docker build --rm -t quay.io/pypa/manylinux1_$PLATFORM:$TRAVIS_COMMIT -f docker/Dockerfile-$PLATFORM docker/
2728

2829

docker/Dockerfile-x86_64

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@ ENV PATH /opt/rh/devtoolset-2/root/usr/bin:$PATH
88
ENV LD_LIBRARY_PATH /opt/rh/devtoolset-2/root/usr/lib64:/opt/rh/devtoolset-2/root/usr/lib:/usr/local/lib64:/usr/local/lib
99
ENV PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
1010

11+
COPY vsyscall_emu/vsyscall_trace /usr/local/sbin/vsyscall_trace
12+
1113
COPY build_scripts /build_scripts
1214
RUN bash build_scripts/build.sh && rm -r build_scripts
1315

1416
ENV SSL_CERT_FILE=/opt/_internal/certs.pem
1517

18+
ENTRYPOINT ["/usr/local/sbin/vsyscall_trace"]
19+
1620
CMD ["/bin/bash"]

docker/vsyscall_emu/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
vsyscall_trace

docker/vsyscall_emu/Makefile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
ifeq ($(PLATFORM),x86_64)
2+
all: vsyscall_trace
3+
else
4+
all:
5+
endif
6+
7+
vsyscall_trace: vsyscall_trace.c
8+
$(CC) -o $@ $< -ldl
9+
10+
clean:
11+
$(RM) -f vsyscall_trace
12+
13+
.PHONY: clean

docker/vsyscall_emu/vsyscall_trace.c

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
#define _GNU_SOURCE
2+
#include <sys/auxv.h>
3+
#include <sys/ptrace.h>
4+
#include <sys/types.h>
5+
#include <sys/stat.h>
6+
#include <sys/wait.h>
7+
#include <sys/user.h>
8+
#include <dlfcn.h>
9+
#include <errno.h>
10+
#include <fcntl.h>
11+
#include <signal.h>
12+
#include <stdlib.h>
13+
#include <stdio.h>
14+
#include <string.h>
15+
#include <unistd.h>
16+
17+
#ifdef DEBUG
18+
#define debug_printf printf
19+
#else
20+
#define debug_printf(...) 0
21+
#endif
22+
23+
const unsigned long VSYS_gettimeofday = 0xffffffffff600000,
24+
VSYS_time = 0xffffffffff600400,
25+
VSYS_getcpu = 0xffffffffff600800;
26+
unsigned long VDSO_gettimeofday, VDSO_time, VDSO_getcpu;
27+
28+
unsigned long vdso_address(pid_t pid) {
29+
char *filename;
30+
asprintf(&filename, "/proc/%d/auxv", pid);
31+
int fd = open(filename, O_RDONLY);
32+
unsigned long buf[128];
33+
int i;
34+
read(fd, buf, sizeof(buf));
35+
close(fd);
36+
free(filename);
37+
for (i = 0; i < 128; i += 2) {
38+
if (buf[i] == AT_SYSINFO_EHDR) {
39+
return buf[i+1];
40+
} else if (buf[i] == 0) {
41+
return 0;
42+
}
43+
}
44+
}
45+
46+
int handle_vsyscall(pid_t pid) {
47+
struct user_regs_struct regs;
48+
ptrace(PTRACE_GETREGS, pid, 0, &regs);
49+
if ((regs.rip & 0xfffffffffffff0ff) == 0xffffffffff600000) {
50+
debug_printf("handling vsyscall for %d\n", pid);
51+
unsigned long vdso = vdso_address(pid);
52+
if (vdso_address == 0) {
53+
debug_printf("couldn't find vdso\n");
54+
return 0;
55+
}
56+
57+
if (regs.rip == VSYS_gettimeofday) {
58+
regs.rip = vdso | VDSO_gettimeofday;
59+
} else if (regs.rip == VSYS_time) {
60+
regs.rip = vdso | VDSO_time;
61+
} else if (regs.rip == VSYS_getcpu) {
62+
regs.rip = vdso | VDSO_getcpu;
63+
} else {
64+
debug_printf("invalid vsyscall %x\n", regs.rip);
65+
return 0;
66+
}
67+
ptrace(PTRACE_SETREGS, pid, 0, &regs);
68+
return 1;
69+
}
70+
return 0;
71+
}
72+
73+
int main(int argc, char *argv[]) {
74+
pid_t pid, child_pid = 0;
75+
int wstatus, child_wstatus = 0;
76+
77+
if (argc < 2) {
78+
printf("usage: vsyscall_trace -p <pid>...\n");
79+
printf(" vsyscall_trace <cmd> [args...]\n");
80+
return 1;
81+
}
82+
83+
if (strcmp(argv[1], "-p") == 0) {
84+
int i;
85+
for (i = 2; i < argc; i++) {
86+
pid = atoi(argv[i]);
87+
if (ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE) != 0) {
88+
perror("PTRACE_SEIZE");
89+
return 1;
90+
}
91+
}
92+
} else {
93+
child_pid = fork();
94+
if (child_pid == -1) {
95+
perror("fork");
96+
return 1;
97+
} else if (child_pid == 0) {
98+
((time_t (*)(time_t *))VSYS_time)(NULL);
99+
return 0;
100+
} else {
101+
waitpid(child_pid, &wstatus, 0);
102+
if (WIFEXITED(wstatus)) {
103+
/* vsyscall page exists, get out of the way */
104+
execvp(argv[1], &argv[1]);
105+
perror("execvp");
106+
return 1;
107+
}
108+
}
109+
110+
child_pid = fork();
111+
if (child_pid == -1) {
112+
perror("fork");
113+
return 1;
114+
} else if (child_pid == 0) {
115+
raise(SIGSTOP);
116+
execvp(argv[1], &argv[1]);
117+
perror("execvp");
118+
return 1;
119+
} else {
120+
if (ptrace(PTRACE_SEIZE, child_pid, 0, PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE) != 0) {
121+
perror("PTRACE_SEIZE");
122+
if (errno == EPERM) {
123+
fprintf(stderr, "Error: no kernel vsyscall support and ptrace is disabled.\n");
124+
fprintf(stderr, "Your kernel does not provide vsyscall emulation, and we cannot\n");
125+
fprintf(stderr, "work around this because ptrace is prohibited inside this container.\n");
126+
fprintf(stderr, "Either permit ptrace for this container (e.g., for Docker, use\n");
127+
fprintf(stderr, "docker run --security-opt=seccomp:unconfined) or boot your kernel\n");
128+
fprintf(stderr, "with vsyscall=emulate.\n");
129+
}
130+
kill(child_pid, SIGKILL);
131+
return 1;
132+
}
133+
fprintf(stderr, "Warning: using ptrace-based vsyscall emulation.\n");
134+
fprintf(stderr, "This container contains old binaries which require the use of the legacy\n");
135+
fprintf(stderr, "'vsyscall' feature of the Linux kernel, and your kernel does not provide\n");
136+
fprintf(stderr, "vsyscall emulation. We will attempt to emulate vsyscalls ourselves using\n");
137+
fprintf(stderr, "ptrace, but performance may suffer and other tools that use ptrace (e.g.,\n");
138+
fprintf(stderr, "gdb and strace) will not work.\n");
139+
fprintf(stderr, "To avoid this emulation, please boot your kernel with vsyscall=emulate.\n");
140+
kill(child_pid, SIGCONT);
141+
}
142+
}
143+
144+
void *vdso = dlopen("linux-vdso.so.1", RTLD_LAZY | RTLD_NOLOAD);
145+
VDSO_gettimeofday = (unsigned long)dlsym(vdso, "__vdso_gettimeofday") & 0xfff;
146+
VDSO_time = (unsigned long)dlsym(vdso, "__vdso_time") & 0xfff;
147+
VDSO_getcpu = (unsigned long)dlsym(vdso, "__vdso_getcpu") & 0xfff;
148+
149+
while ((pid = waitpid(-1, &wstatus, 0)) != -1) {
150+
if (WIFSTOPPED(wstatus)) {
151+
if (WSTOPSIG(wstatus) == SIGSEGV && handle_vsyscall(pid)) {
152+
ptrace(PTRACE_CONT, pid, 0, 0);
153+
} else {
154+
ptrace(PTRACE_CONT, pid, 0, WSTOPSIG(wstatus));
155+
}
156+
} else if (pid == child_pid && WIFEXITED(wstatus)) {
157+
child_wstatus = wstatus;
158+
}
159+
}
160+
if (errno != ECHILD) {
161+
perror("waitpid");
162+
return 1;
163+
}
164+
if (WIFSIGNALED(wstatus)) {
165+
raise(WTERMSIG(wstatus));
166+
return 1;
167+
} else {
168+
return WEXITSTATUS(wstatus);
169+
}
170+
}

0 commit comments

Comments
 (0)