168d75effSDimitry Andric //===-- sanitizer_linux_s390.cpp ------------------------------------------===//
268d75effSDimitry Andric //
368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
668d75effSDimitry Andric //
768d75effSDimitry Andric //===----------------------------------------------------------------------===//
868d75effSDimitry Andric //
968d75effSDimitry Andric // This file is shared between AddressSanitizer and ThreadSanitizer
1068d75effSDimitry Andric // run-time libraries and implements s390-linux-specific functions from
1168d75effSDimitry Andric // sanitizer_libc.h.
1268d75effSDimitry Andric //===----------------------------------------------------------------------===//
1368d75effSDimitry Andric 
1468d75effSDimitry Andric #include "sanitizer_platform.h"
1568d75effSDimitry Andric 
1668d75effSDimitry Andric #if SANITIZER_LINUX && SANITIZER_S390
1768d75effSDimitry Andric 
185ffd83dbSDimitry Andric #  include <dlfcn.h>
1968d75effSDimitry Andric #  include <errno.h>
2068d75effSDimitry Andric #  include <sys/syscall.h>
2168d75effSDimitry Andric #  include <sys/utsname.h>
2268d75effSDimitry Andric #  include <unistd.h>
2368d75effSDimitry Andric 
245ffd83dbSDimitry Andric #  include "sanitizer_libc.h"
255ffd83dbSDimitry Andric #  include "sanitizer_linux.h"
265ffd83dbSDimitry Andric 
2768d75effSDimitry Andric namespace __sanitizer {
2868d75effSDimitry Andric 
2968d75effSDimitry Andric // --------------- sanitizer_libc.h
internal_mmap(void * addr,uptr length,int prot,int flags,int fd,u64 offset)3068d75effSDimitry Andric uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
31480093f4SDimitry Andric                    u64 offset) {
3268d75effSDimitry Andric   struct s390_mmap_params {
3368d75effSDimitry Andric     unsigned long addr;
3468d75effSDimitry Andric     unsigned long length;
3568d75effSDimitry Andric     unsigned long prot;
3668d75effSDimitry Andric     unsigned long flags;
3768d75effSDimitry Andric     unsigned long fd;
3868d75effSDimitry Andric     unsigned long offset;
3968d75effSDimitry Andric   } params = {
40*5f757f3fSDimitry Andric       (unsigned long)addr,   (unsigned long)length, (unsigned long)prot,
41*5f757f3fSDimitry Andric       (unsigned long)flags,  (unsigned long)fd,
4268d75effSDimitry Andric #  ifdef __s390x__
4368d75effSDimitry Andric       (unsigned long)offset,
4468d75effSDimitry Andric #  else
4568d75effSDimitry Andric     (unsigned long)(offset / 4096),
4668d75effSDimitry Andric #  endif
4768d75effSDimitry Andric   };
4868d75effSDimitry Andric #  ifdef __s390x__
4968d75effSDimitry Andric   return syscall(__NR_mmap, &params);
5068d75effSDimitry Andric #  else
5168d75effSDimitry Andric   return syscall(__NR_mmap2, &params);
5268d75effSDimitry Andric #  endif
5368d75effSDimitry Andric }
5468d75effSDimitry Andric 
internal_clone(int (* fn)(void *),void * child_stack,int flags,void * arg,int * parent_tidptr,void * newtls,int * child_tidptr)5568d75effSDimitry Andric uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
5668d75effSDimitry Andric                     int *parent_tidptr, void *newtls, int *child_tidptr) {
57349cc55cSDimitry Andric   if (!fn || !child_stack) {
58349cc55cSDimitry Andric     errno = EINVAL;
59349cc55cSDimitry Andric     return -1;
60349cc55cSDimitry Andric   }
6168d75effSDimitry Andric   CHECK_EQ(0, (uptr)child_stack % 16);
6268d75effSDimitry Andric   // Minimum frame size.
6368d75effSDimitry Andric #  ifdef __s390x__
6468d75effSDimitry Andric   child_stack = (char *)child_stack - 160;
6568d75effSDimitry Andric #  else
6668d75effSDimitry Andric   child_stack = (char *)child_stack - 96;
6768d75effSDimitry Andric #  endif
6868d75effSDimitry Andric   // Terminate unwind chain.
6968d75effSDimitry Andric   ((unsigned long *)child_stack)[0] = 0;
7068d75effSDimitry Andric   // And pass parameters.
7168d75effSDimitry Andric   ((unsigned long *)child_stack)[1] = (uptr)fn;
7268d75effSDimitry Andric   ((unsigned long *)child_stack)[2] = (uptr)arg;
73349cc55cSDimitry Andric   register uptr res __asm__("r2");
7468d75effSDimitry Andric   register void *__cstack __asm__("r2") = child_stack;
75349cc55cSDimitry Andric   register long __flags __asm__("r3") = flags;
7668d75effSDimitry Andric   register int *__ptidptr __asm__("r4") = parent_tidptr;
7768d75effSDimitry Andric   register int *__ctidptr __asm__("r5") = child_tidptr;
7868d75effSDimitry Andric   register void *__newtls __asm__("r6") = newtls;
7968d75effSDimitry Andric 
8068d75effSDimitry Andric   __asm__ __volatile__(
8168d75effSDimitry Andric       /* Clone. */
8268d75effSDimitry Andric       "svc    %1\n"
8368d75effSDimitry Andric 
8468d75effSDimitry Andric   /* if (%r2 != 0)
8568d75effSDimitry Andric    *   return;
8668d75effSDimitry Andric    */
8768d75effSDimitry Andric #  ifdef __s390x__
8868d75effSDimitry Andric       "cghi   %%r2, 0\n"
8968d75effSDimitry Andric #  else
9068d75effSDimitry Andric       "chi    %%r2, 0\n"
9168d75effSDimitry Andric #  endif
9268d75effSDimitry Andric       "jne    1f\n"
9368d75effSDimitry Andric 
9468d75effSDimitry Andric   /* Call "fn(arg)". */
9568d75effSDimitry Andric #  ifdef __s390x__
9668d75effSDimitry Andric       "lmg    %%r1, %%r2, 8(%%r15)\n"
9768d75effSDimitry Andric #  else
9868d75effSDimitry Andric       "lm     %%r1, %%r2, 4(%%r15)\n"
9968d75effSDimitry Andric #  endif
10068d75effSDimitry Andric       "basr   %%r14, %%r1\n"
10168d75effSDimitry Andric 
10268d75effSDimitry Andric       /* Call _exit(%r2). */
10368d75effSDimitry Andric       "svc %2\n"
10468d75effSDimitry Andric 
10568d75effSDimitry Andric       /* Return to parent. */
10668d75effSDimitry Andric       "1:\n"
10768d75effSDimitry Andric       : "=r"(res)
108*5f757f3fSDimitry Andric       : "i"(__NR_clone), "i"(__NR_exit), "r"(__cstack), "r"(__flags),
109*5f757f3fSDimitry Andric         "r"(__ptidptr), "r"(__ctidptr), "r"(__newtls)
11068d75effSDimitry Andric       : "memory", "cc");
111349cc55cSDimitry Andric   if (res >= (uptr)-4095) {
112349cc55cSDimitry Andric     errno = -res;
113349cc55cSDimitry Andric     return -1;
114349cc55cSDimitry Andric   }
11568d75effSDimitry Andric   return res;
11668d75effSDimitry Andric }
11768d75effSDimitry Andric 
11868d75effSDimitry Andric #  if SANITIZER_S390_64
FixedCVE_2016_2143()11968d75effSDimitry Andric static bool FixedCVE_2016_2143() {
12068d75effSDimitry Andric   // Try to determine if the running kernel has a fix for CVE-2016-2143,
12168d75effSDimitry Andric   // return false if in doubt (better safe than sorry).  Distros may want to
12268d75effSDimitry Andric   // adjust this for their own kernels.
12368d75effSDimitry Andric   struct utsname buf;
12468d75effSDimitry Andric   unsigned int major, minor, patch = 0;
12568d75effSDimitry Andric   // This should never fail, but just in case...
1265ffd83dbSDimitry Andric   if (internal_uname(&buf))
12768d75effSDimitry Andric     return false;
12868d75effSDimitry Andric   const char *ptr = buf.release;
12968d75effSDimitry Andric   major = internal_simple_strtoll(ptr, &ptr, 10);
13068d75effSDimitry Andric   // At least first 2 should be matched.
13168d75effSDimitry Andric   if (ptr[0] != '.')
13268d75effSDimitry Andric     return false;
13368d75effSDimitry Andric   minor = internal_simple_strtoll(ptr + 1, &ptr, 10);
13468d75effSDimitry Andric   // Third is optional.
13568d75effSDimitry Andric   if (ptr[0] == '.')
13668d75effSDimitry Andric     patch = internal_simple_strtoll(ptr + 1, &ptr, 10);
13768d75effSDimitry Andric   if (major < 3) {
13868d75effSDimitry Andric     if (major == 2 && minor == 6 && patch == 32 && ptr[0] == '-' &&
13968d75effSDimitry Andric         internal_strstr(ptr, ".el6")) {
14068d75effSDimitry Andric       // Check RHEL6
14168d75effSDimitry Andric       int r1 = internal_simple_strtoll(ptr + 1, &ptr, 10);
14268d75effSDimitry Andric       if (r1 >= 657)  // 2.6.32-657.el6 or later
14368d75effSDimitry Andric         return true;
14468d75effSDimitry Andric       if (r1 == 642 && ptr[0] == '.') {
14568d75effSDimitry Andric         int r2 = internal_simple_strtoll(ptr + 1, &ptr, 10);
14668d75effSDimitry Andric         if (r2 >= 9)  // 2.6.32-642.9.1.el6 or later
14768d75effSDimitry Andric           return true;
14868d75effSDimitry Andric       }
14968d75effSDimitry Andric     }
15068d75effSDimitry Andric     // <3.0 is bad.
15168d75effSDimitry Andric     return false;
15268d75effSDimitry Andric   } else if (major == 3) {
15368d75effSDimitry Andric     // 3.2.79+ is OK.
15468d75effSDimitry Andric     if (minor == 2 && patch >= 79)
15568d75effSDimitry Andric       return true;
15668d75effSDimitry Andric     // 3.12.58+ is OK.
15768d75effSDimitry Andric     if (minor == 12 && patch >= 58)
15868d75effSDimitry Andric       return true;
15968d75effSDimitry Andric     if (minor == 10 && patch == 0 && ptr[0] == '-' &&
16068d75effSDimitry Andric         internal_strstr(ptr, ".el7")) {
16168d75effSDimitry Andric       // Check RHEL7
16268d75effSDimitry Andric       int r1 = internal_simple_strtoll(ptr + 1, &ptr, 10);
16368d75effSDimitry Andric       if (r1 >= 426)  // 3.10.0-426.el7 or later
16468d75effSDimitry Andric         return true;
16568d75effSDimitry Andric       if (r1 == 327 && ptr[0] == '.') {
16668d75effSDimitry Andric         int r2 = internal_simple_strtoll(ptr + 1, &ptr, 10);
16768d75effSDimitry Andric         if (r2 >= 27)  // 3.10.0-327.27.1.el7 or later
16868d75effSDimitry Andric           return true;
16968d75effSDimitry Andric       }
17068d75effSDimitry Andric     }
17168d75effSDimitry Andric     // Otherwise, bad.
17268d75effSDimitry Andric     return false;
17368d75effSDimitry Andric   } else if (major == 4) {
17468d75effSDimitry Andric     // 4.1.21+ is OK.
17568d75effSDimitry Andric     if (minor == 1 && patch >= 21)
17668d75effSDimitry Andric       return true;
17768d75effSDimitry Andric     // 4.4.6+ is OK.
17868d75effSDimitry Andric     if (minor == 4 && patch >= 6)
17968d75effSDimitry Andric       return true;
18068d75effSDimitry Andric     if (minor == 4 && patch == 0 && ptr[0] == '-' &&
18168d75effSDimitry Andric         internal_strstr(buf.version, "Ubuntu")) {
18268d75effSDimitry Andric       // Check Ubuntu 16.04
18368d75effSDimitry Andric       int r1 = internal_simple_strtoll(ptr + 1, &ptr, 10);
18468d75effSDimitry Andric       if (r1 >= 13)  // 4.4.0-13 or later
18568d75effSDimitry Andric         return true;
18668d75effSDimitry Andric     }
18768d75effSDimitry Andric     // Otherwise, OK if 4.5+.
18868d75effSDimitry Andric     return minor >= 5;
18968d75effSDimitry Andric   } else {
19068d75effSDimitry Andric     // Linux 5 and up are fine.
19168d75effSDimitry Andric     return true;
19268d75effSDimitry Andric   }
19368d75effSDimitry Andric }
19468d75effSDimitry Andric 
AvoidCVE_2016_2143()19568d75effSDimitry Andric void AvoidCVE_2016_2143() {
19668d75effSDimitry Andric   // Older kernels are affected by CVE-2016-2143 - they will crash hard
19768d75effSDimitry Andric   // if someone uses 4-level page tables (ie. virtual addresses >= 4TB)
19868d75effSDimitry Andric   // and fork() in the same process.  Unfortunately, sanitizers tend to
19968d75effSDimitry Andric   // require such addresses.  Since this is very likely to crash the whole
20068d75effSDimitry Andric   // machine (sanitizers themselves use fork() for llvm-symbolizer, for one),
20168d75effSDimitry Andric   // abort the process at initialization instead.
20268d75effSDimitry Andric   if (FixedCVE_2016_2143())
20368d75effSDimitry Andric     return;
20468d75effSDimitry Andric   if (GetEnv("SANITIZER_IGNORE_CVE_2016_2143"))
20568d75effSDimitry Andric     return;
20668d75effSDimitry Andric   Report(
207*5f757f3fSDimitry Andric       "ERROR: Your kernel seems to be vulnerable to CVE-2016-2143.  Using "
208*5f757f3fSDimitry Andric       "ASan,\n"
20968d75effSDimitry Andric       "MSan, TSan, DFSan or LSan with such kernel can and will crash your\n"
21068d75effSDimitry Andric       "machine, or worse.\n"
21168d75effSDimitry Andric       "\n"
21268d75effSDimitry Andric       "If you are certain your kernel is not vulnerable (you have compiled it\n"
21368d75effSDimitry Andric       "yourself, or are using an unrecognized distribution kernel), you can\n"
21468d75effSDimitry Andric       "override this safety check by exporting SANITIZER_IGNORE_CVE_2016_2143\n"
21568d75effSDimitry Andric       "with any value.\n");
21668d75effSDimitry Andric   Die();
21768d75effSDimitry Andric }
21868d75effSDimitry Andric #  endif
21968d75effSDimitry Andric 
22068d75effSDimitry Andric }  // namespace __sanitizer
22168d75effSDimitry Andric 
22268d75effSDimitry Andric #endif  // SANITIZER_LINUX && SANITIZER_S390
223