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