]>
Commit | Line | Data |
---|---|---|
7c34048e GKH |
1 | From 02e425668f5c9deb42787d10001a3b605993ad15 Mon Sep 17 00:00:00 2001 |
2 | From: Andy Lutomirski <luto@kernel.org> | |
3 | Date: Wed, 3 Oct 2018 16:23:49 -0700 | |
4 | Subject: x86/vdso: Fix vDSO syscall fallback asm constraint regression | |
5 | ||
6 | From: Andy Lutomirski <luto@kernel.org> | |
7 | ||
8 | commit 02e425668f5c9deb42787d10001a3b605993ad15 upstream. | |
9 | ||
10 | When I added the missing memory outputs, I failed to update the | |
11 | index of the first argument (ebx) on 32-bit builds, which broke the | |
12 | fallbacks. Somehow I must have screwed up my testing or gotten | |
13 | lucky. | |
14 | ||
15 | Add another test to cover gettimeofday() as well. | |
16 | ||
17 | Signed-off-by: Andy Lutomirski <luto@kernel.org> | |
18 | Cc: Linus Torvalds <torvalds@linux-foundation.org> | |
19 | Cc: Peter Zijlstra <peterz@infradead.org> | |
20 | Cc: Thomas Gleixner <tglx@linutronix.de> | |
21 | Cc: stable@vger.kernel.org | |
22 | Fixes: 715bd9d12f84 ("x86/vdso: Fix asm constraints on vDSO syscall fallbacks") | |
23 | Link: http://lkml.kernel.org/r/21bd45ab04b6d838278fa5bebfa9163eceffa13c.1538608971.git.luto@kernel.org | |
24 | Signed-off-by: Ingo Molnar <mingo@kernel.org> | |
25 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
26 | ||
27 | --- | |
28 | arch/x86/entry/vdso/vclock_gettime.c | 8 +-- | |
29 | tools/testing/selftests/x86/test_vdso.c | 73 ++++++++++++++++++++++++++++++++ | |
30 | 2 files changed, 77 insertions(+), 4 deletions(-) | |
31 | ||
32 | --- a/arch/x86/entry/vdso/vclock_gettime.c | |
33 | +++ b/arch/x86/entry/vdso/vclock_gettime.c | |
34 | @@ -68,11 +68,11 @@ notrace static long vdso_fallback_gettim | |
35 | ||
36 | asm ( | |
37 | "mov %%ebx, %%edx \n" | |
38 | - "mov %2, %%ebx \n" | |
39 | + "mov %[clock], %%ebx \n" | |
40 | "call __kernel_vsyscall \n" | |
41 | "mov %%edx, %%ebx \n" | |
42 | : "=a" (ret), "=m" (*ts) | |
43 | - : "0" (__NR_clock_gettime), "g" (clock), "c" (ts) | |
44 | + : "0" (__NR_clock_gettime), [clock] "g" (clock), "c" (ts) | |
45 | : "memory", "edx"); | |
46 | return ret; | |
47 | } | |
48 | @@ -83,11 +83,11 @@ notrace static long vdso_fallback_gtod(s | |
49 | ||
50 | asm ( | |
51 | "mov %%ebx, %%edx \n" | |
52 | - "mov %2, %%ebx \n" | |
53 | + "mov %[tv], %%ebx \n" | |
54 | "call __kernel_vsyscall \n" | |
55 | "mov %%edx, %%ebx \n" | |
56 | : "=a" (ret), "=m" (*tv), "=m" (*tz) | |
57 | - : "0" (__NR_gettimeofday), "g" (tv), "c" (tz) | |
58 | + : "0" (__NR_gettimeofday), [tv] "g" (tv), "c" (tz) | |
59 | : "memory", "edx"); | |
60 | return ret; | |
61 | } | |
62 | --- a/tools/testing/selftests/x86/test_vdso.c | |
63 | +++ b/tools/testing/selftests/x86/test_vdso.c | |
64 | @@ -36,6 +36,10 @@ typedef int (*vgettime_t)(clockid_t, str | |
65 | ||
66 | vgettime_t vdso_clock_gettime; | |
67 | ||
68 | +typedef long (*vgtod_t)(struct timeval *tv, struct timezone *tz); | |
69 | + | |
70 | +vgtod_t vdso_gettimeofday; | |
71 | + | |
72 | typedef long (*getcpu_t)(unsigned *, unsigned *, void *); | |
73 | ||
74 | getcpu_t vgetcpu; | |
75 | @@ -104,6 +108,11 @@ static void fill_function_pointers() | |
76 | vdso_clock_gettime = (vgettime_t)dlsym(vdso, "__vdso_clock_gettime"); | |
77 | if (!vdso_clock_gettime) | |
78 | printf("Warning: failed to find clock_gettime in vDSO\n"); | |
79 | + | |
80 | + vdso_gettimeofday = (vgtod_t)dlsym(vdso, "__vdso_gettimeofday"); | |
81 | + if (!vdso_gettimeofday) | |
82 | + printf("Warning: failed to find gettimeofday in vDSO\n"); | |
83 | + | |
84 | } | |
85 | ||
86 | static long sys_getcpu(unsigned * cpu, unsigned * node, | |
87 | @@ -117,6 +126,11 @@ static inline int sys_clock_gettime(cloc | |
88 | return syscall(__NR_clock_gettime, id, ts); | |
89 | } | |
90 | ||
91 | +static inline int sys_gettimeofday(struct timeval *tv, struct timezone *tz) | |
92 | +{ | |
93 | + return syscall(__NR_gettimeofday, tv, tz); | |
94 | +} | |
95 | + | |
96 | static void test_getcpu(void) | |
97 | { | |
98 | printf("[RUN]\tTesting getcpu...\n"); | |
99 | @@ -177,6 +191,14 @@ static bool ts_leq(const struct timespec | |
100 | return a->tv_nsec <= b->tv_nsec; | |
101 | } | |
102 | ||
103 | +static bool tv_leq(const struct timeval *a, const struct timeval *b) | |
104 | +{ | |
105 | + if (a->tv_sec != b->tv_sec) | |
106 | + return a->tv_sec < b->tv_sec; | |
107 | + else | |
108 | + return a->tv_usec <= b->tv_usec; | |
109 | +} | |
110 | + | |
111 | static char const * const clocknames[] = { | |
112 | [0] = "CLOCK_REALTIME", | |
113 | [1] = "CLOCK_MONOTONIC", | |
114 | @@ -248,11 +270,62 @@ static void test_clock_gettime(void) | |
115 | test_one_clock_gettime(INT_MAX, "invalid"); | |
116 | } | |
117 | ||
118 | +static void test_gettimeofday(void) | |
119 | +{ | |
120 | + struct timeval start, vdso, end; | |
121 | + struct timezone sys_tz, vdso_tz; | |
122 | + int vdso_ret, end_ret; | |
123 | + | |
124 | + if (!vdso_gettimeofday) | |
125 | + return; | |
126 | + | |
127 | + printf("[RUN]\tTesting gettimeofday...\n"); | |
128 | + | |
129 | + if (sys_gettimeofday(&start, &sys_tz) < 0) { | |
130 | + printf("[FAIL]\tsys_gettimeofday failed (%d)\n", errno); | |
131 | + nerrs++; | |
132 | + return; | |
133 | + } | |
134 | + | |
135 | + vdso_ret = vdso_gettimeofday(&vdso, &vdso_tz); | |
136 | + end_ret = sys_gettimeofday(&end, NULL); | |
137 | + | |
138 | + if (vdso_ret != 0 || end_ret != 0) { | |
139 | + printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n", | |
140 | + vdso_ret, errno); | |
141 | + nerrs++; | |
142 | + return; | |
143 | + } | |
144 | + | |
145 | + printf("\t%llu.%06ld %llu.%06ld %llu.%06ld\n", | |
146 | + (unsigned long long)start.tv_sec, start.tv_usec, | |
147 | + (unsigned long long)vdso.tv_sec, vdso.tv_usec, | |
148 | + (unsigned long long)end.tv_sec, end.tv_usec); | |
149 | + | |
150 | + if (!tv_leq(&start, &vdso) || !tv_leq(&vdso, &end)) { | |
151 | + printf("[FAIL]\tTimes are out of sequence\n"); | |
152 | + nerrs++; | |
153 | + } | |
154 | + | |
155 | + if (sys_tz.tz_minuteswest == vdso_tz.tz_minuteswest && | |
156 | + sys_tz.tz_dsttime == vdso_tz.tz_dsttime) { | |
157 | + printf("[OK]\ttimezones match: minuteswest=%d, dsttime=%d\n", | |
158 | + sys_tz.tz_minuteswest, sys_tz.tz_dsttime); | |
159 | + } else { | |
160 | + printf("[FAIL]\ttimezones do not match\n"); | |
161 | + nerrs++; | |
162 | + } | |
163 | + | |
164 | + /* And make sure that passing NULL for tz doesn't crash. */ | |
165 | + vdso_gettimeofday(&vdso, NULL); | |
166 | +} | |
167 | + | |
168 | int main(int argc, char **argv) | |
169 | { | |
170 | fill_function_pointers(); | |
171 | ||
172 | test_clock_gettime(); | |
173 | + test_gettimeofday(); | |
174 | ||
175 | /* | |
176 | * Test getcpu() last so that, if something goes wrong setting affinity, |