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