1 | //===-- sanitizer_linux_s390.cpp ------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file is shared between AddressSanitizer and ThreadSanitizer |
10 | // run-time libraries and implements s390-linux-specific functions from |
11 | // sanitizer_libc.h. |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "sanitizer_platform.h" |
15 | |
16 | #if SANITIZER_LINUX && SANITIZER_S390 |
17 | |
18 | # include <dlfcn.h> |
19 | # include <errno.h> |
20 | # include <sys/syscall.h> |
21 | # include <sys/utsname.h> |
22 | # include <unistd.h> |
23 | |
24 | # include "sanitizer_libc.h" |
25 | # include "sanitizer_linux.h" |
26 | |
27 | namespace __sanitizer { |
28 | |
29 | // --------------- sanitizer_libc.h |
30 | uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd, |
31 | u64 offset) { |
32 | struct s390_mmap_params { |
33 | unsigned long addr; |
34 | unsigned long length; |
35 | unsigned long prot; |
36 | unsigned long flags; |
37 | unsigned long fd; |
38 | unsigned long offset; |
39 | } params = { |
40 | (unsigned long)addr, (unsigned long)length, (unsigned long)prot, |
41 | (unsigned long)flags, (unsigned long)fd, |
42 | # ifdef __s390x__ |
43 | (unsigned long)offset, |
44 | # else |
45 | (unsigned long)(offset / 4096), |
46 | # endif |
47 | }; |
48 | # ifdef __s390x__ |
49 | return syscall(__NR_mmap, ¶ms); |
50 | # else |
51 | return syscall(__NR_mmap2, ¶ms); |
52 | # endif |
53 | } |
54 | |
55 | uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, |
56 | int *parent_tidptr, void *newtls, int *child_tidptr) { |
57 | if (!fn || !child_stack) { |
58 | errno = EINVAL; |
59 | return -1; |
60 | } |
61 | CHECK_EQ(0, (uptr)child_stack % 16); |
62 | // Minimum frame size. |
63 | # ifdef __s390x__ |
64 | child_stack = (char *)child_stack - 160; |
65 | # else |
66 | child_stack = (char *)child_stack - 96; |
67 | # endif |
68 | // Terminate unwind chain. |
69 | ((unsigned long *)child_stack)[0] = 0; |
70 | // And pass parameters. |
71 | ((unsigned long *)child_stack)[1] = (uptr)fn; |
72 | ((unsigned long *)child_stack)[2] = (uptr)arg; |
73 | register uptr res __asm__("r2" ); |
74 | register void *__cstack __asm__("r2" ) = child_stack; |
75 | register long __flags __asm__("r3" ) = flags; |
76 | register int *__ptidptr __asm__("r4" ) = parent_tidptr; |
77 | register int *__ctidptr __asm__("r5" ) = child_tidptr; |
78 | register void *__newtls __asm__("r6" ) = newtls; |
79 | |
80 | __asm__ __volatile__( |
81 | /* Clone. */ |
82 | "svc %1\n" |
83 | |
84 | /* if (%r2 != 0) |
85 | * return; |
86 | */ |
87 | # ifdef __s390x__ |
88 | "cghi %%r2, 0\n" |
89 | # else |
90 | "chi %%r2, 0\n" |
91 | # endif |
92 | "jne 1f\n" |
93 | |
94 | /* Call "fn(arg)". */ |
95 | # ifdef __s390x__ |
96 | "lmg %%r1, %%r2, 8(%%r15)\n" |
97 | # else |
98 | "lm %%r1, %%r2, 4(%%r15)\n" |
99 | # endif |
100 | "basr %%r14, %%r1\n" |
101 | |
102 | /* Call _exit(%r2). */ |
103 | "svc %2\n" |
104 | |
105 | /* Return to parent. */ |
106 | "1:\n" |
107 | : "=r" (res) |
108 | : "i" (__NR_clone), "i" (__NR_exit), "r" (__cstack), "r" (__flags), |
109 | "r" (__ptidptr), "r" (__ctidptr), "r" (__newtls) |
110 | : "memory" , "cc" ); |
111 | if (res >= (uptr)-4095) { |
112 | errno = -res; |
113 | return -1; |
114 | } |
115 | return res; |
116 | } |
117 | |
118 | # if SANITIZER_S390_64 |
119 | static bool FixedCVE_2016_2143() { |
120 | // Try to determine if the running kernel has a fix for CVE-2016-2143, |
121 | // return false if in doubt (better safe than sorry). Distros may want to |
122 | // adjust this for their own kernels. |
123 | struct utsname buf; |
124 | unsigned int major, minor, patch = 0; |
125 | // This should never fail, but just in case... |
126 | if (internal_uname(&buf)) |
127 | return false; |
128 | const char *ptr = buf.release; |
129 | major = internal_simple_strtoll(ptr, &ptr, 10); |
130 | // At least first 2 should be matched. |
131 | if (ptr[0] != '.') |
132 | return false; |
133 | minor = internal_simple_strtoll(ptr + 1, &ptr, 10); |
134 | // Third is optional. |
135 | if (ptr[0] == '.') |
136 | patch = internal_simple_strtoll(ptr + 1, &ptr, 10); |
137 | if (major < 3) { |
138 | if (major == 2 && minor == 6 && patch == 32 && ptr[0] == '-' && |
139 | internal_strstr(ptr, ".el6" )) { |
140 | // Check RHEL6 |
141 | int r1 = internal_simple_strtoll(ptr + 1, &ptr, 10); |
142 | if (r1 >= 657) // 2.6.32-657.el6 or later |
143 | return true; |
144 | if (r1 == 642 && ptr[0] == '.') { |
145 | int r2 = internal_simple_strtoll(ptr + 1, &ptr, 10); |
146 | if (r2 >= 9) // 2.6.32-642.9.1.el6 or later |
147 | return true; |
148 | } |
149 | } |
150 | // <3.0 is bad. |
151 | return false; |
152 | } else if (major == 3) { |
153 | // 3.2.79+ is OK. |
154 | if (minor == 2 && patch >= 79) |
155 | return true; |
156 | // 3.12.58+ is OK. |
157 | if (minor == 12 && patch >= 58) |
158 | return true; |
159 | if (minor == 10 && patch == 0 && ptr[0] == '-' && |
160 | internal_strstr(ptr, ".el7" )) { |
161 | // Check RHEL7 |
162 | int r1 = internal_simple_strtoll(ptr + 1, &ptr, 10); |
163 | if (r1 >= 426) // 3.10.0-426.el7 or later |
164 | return true; |
165 | if (r1 == 327 && ptr[0] == '.') { |
166 | int r2 = internal_simple_strtoll(ptr + 1, &ptr, 10); |
167 | if (r2 >= 27) // 3.10.0-327.27.1.el7 or later |
168 | return true; |
169 | } |
170 | } |
171 | // Otherwise, bad. |
172 | return false; |
173 | } else if (major == 4) { |
174 | // 4.1.21+ is OK. |
175 | if (minor == 1 && patch >= 21) |
176 | return true; |
177 | // 4.4.6+ is OK. |
178 | if (minor == 4 && patch >= 6) |
179 | return true; |
180 | if (minor == 4 && patch == 0 && ptr[0] == '-' && |
181 | internal_strstr(buf.version, "Ubuntu" )) { |
182 | // Check Ubuntu 16.04 |
183 | int r1 = internal_simple_strtoll(ptr + 1, &ptr, 10); |
184 | if (r1 >= 13) // 4.4.0-13 or later |
185 | return true; |
186 | } |
187 | // Otherwise, OK if 4.5+. |
188 | return minor >= 5; |
189 | } else { |
190 | // Linux 5 and up are fine. |
191 | return true; |
192 | } |
193 | } |
194 | |
195 | void AvoidCVE_2016_2143() { |
196 | // Older kernels are affected by CVE-2016-2143 - they will crash hard |
197 | // if someone uses 4-level page tables (ie. virtual addresses >= 4TB) |
198 | // and fork() in the same process. Unfortunately, sanitizers tend to |
199 | // require such addresses. Since this is very likely to crash the whole |
200 | // machine (sanitizers themselves use fork() for llvm-symbolizer, for one), |
201 | // abort the process at initialization instead. |
202 | if (FixedCVE_2016_2143()) |
203 | return; |
204 | if (GetEnv("SANITIZER_IGNORE_CVE_2016_2143" )) |
205 | return; |
206 | Report( |
207 | "ERROR: Your kernel seems to be vulnerable to CVE-2016-2143. Using " |
208 | "ASan,\n" |
209 | "MSan, TSan, DFSan or LSan with such kernel can and will crash your\n" |
210 | "machine, or worse.\n" |
211 | "\n" |
212 | "If you are certain your kernel is not vulnerable (you have compiled it\n" |
213 | "yourself, or are using an unrecognized distribution kernel), you can\n" |
214 | "override this safety check by exporting SANITIZER_IGNORE_CVE_2016_2143\n" |
215 | "with any value.\n" ); |
216 | Die(); |
217 | } |
218 | # endif |
219 | |
220 | } // namespace __sanitizer |
221 | |
222 | #endif // SANITIZER_LINUX && SANITIZER_S390 |
223 | |