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