xref: /openbsd-src/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_linux_s390.cpp (revision 810390e339a5425391477d5d41c78d7cab2424ac)
13cab2bb3Spatrick //===-- sanitizer_linux_s390.cpp ------------------------------------------===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // This file is shared between AddressSanitizer and ThreadSanitizer
103cab2bb3Spatrick // run-time libraries and implements s390-linux-specific functions from
113cab2bb3Spatrick // sanitizer_libc.h.
123cab2bb3Spatrick //===----------------------------------------------------------------------===//
133cab2bb3Spatrick 
143cab2bb3Spatrick #include "sanitizer_platform.h"
153cab2bb3Spatrick 
163cab2bb3Spatrick #if SANITIZER_LINUX && SANITIZER_S390
173cab2bb3Spatrick 
181f9cb04fSpatrick #include <dlfcn.h>
193cab2bb3Spatrick #include <errno.h>
203cab2bb3Spatrick #include <sys/syscall.h>
213cab2bb3Spatrick #include <sys/utsname.h>
223cab2bb3Spatrick #include <unistd.h>
233cab2bb3Spatrick 
241f9cb04fSpatrick #include "sanitizer_libc.h"
251f9cb04fSpatrick #include "sanitizer_linux.h"
261f9cb04fSpatrick 
273cab2bb3Spatrick namespace __sanitizer {
283cab2bb3Spatrick 
293cab2bb3Spatrick // --------------- sanitizer_libc.h
internal_mmap(void * addr,uptr length,int prot,int flags,int fd,u64 offset)303cab2bb3Spatrick uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
313cab2bb3Spatrick                    u64 offset) {
323cab2bb3Spatrick   struct s390_mmap_params {
333cab2bb3Spatrick     unsigned long addr;
343cab2bb3Spatrick     unsigned long length;
353cab2bb3Spatrick     unsigned long prot;
363cab2bb3Spatrick     unsigned long flags;
373cab2bb3Spatrick     unsigned long fd;
383cab2bb3Spatrick     unsigned long offset;
393cab2bb3Spatrick   } params = {
403cab2bb3Spatrick     (unsigned long)addr,
413cab2bb3Spatrick     (unsigned long)length,
423cab2bb3Spatrick     (unsigned long)prot,
433cab2bb3Spatrick     (unsigned long)flags,
443cab2bb3Spatrick     (unsigned long)fd,
453cab2bb3Spatrick # ifdef __s390x__
463cab2bb3Spatrick     (unsigned long)offset,
473cab2bb3Spatrick # else
483cab2bb3Spatrick     (unsigned long)(offset / 4096),
493cab2bb3Spatrick # endif
503cab2bb3Spatrick   };
513cab2bb3Spatrick # ifdef __s390x__
523cab2bb3Spatrick   return syscall(__NR_mmap, &params);
533cab2bb3Spatrick # else
543cab2bb3Spatrick   return syscall(__NR_mmap2, &params);
553cab2bb3Spatrick # endif
563cab2bb3Spatrick }
573cab2bb3Spatrick 
internal_clone(int (* fn)(void *),void * child_stack,int flags,void * arg,int * parent_tidptr,void * newtls,int * child_tidptr)583cab2bb3Spatrick uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
593cab2bb3Spatrick                     int *parent_tidptr, void *newtls, int *child_tidptr) {
60*810390e3Srobert   if (!fn || !child_stack) {
61*810390e3Srobert     errno = EINVAL;
62*810390e3Srobert     return -1;
63*810390e3Srobert   }
643cab2bb3Spatrick   CHECK_EQ(0, (uptr)child_stack % 16);
653cab2bb3Spatrick   // Minimum frame size.
663cab2bb3Spatrick #ifdef __s390x__
673cab2bb3Spatrick   child_stack = (char *)child_stack - 160;
683cab2bb3Spatrick #else
693cab2bb3Spatrick   child_stack = (char *)child_stack - 96;
703cab2bb3Spatrick #endif
713cab2bb3Spatrick   // Terminate unwind chain.
723cab2bb3Spatrick   ((unsigned long *)child_stack)[0] = 0;
733cab2bb3Spatrick   // And pass parameters.
743cab2bb3Spatrick   ((unsigned long *)child_stack)[1] = (uptr)fn;
753cab2bb3Spatrick   ((unsigned long *)child_stack)[2] = (uptr)arg;
76*810390e3Srobert   register uptr res __asm__("r2");
773cab2bb3Spatrick   register void *__cstack      __asm__("r2") = child_stack;
78*810390e3Srobert   register long __flags        __asm__("r3") = flags;
793cab2bb3Spatrick   register int * __ptidptr     __asm__("r4") = parent_tidptr;
803cab2bb3Spatrick   register int * __ctidptr     __asm__("r5") = child_tidptr;
813cab2bb3Spatrick   register void * __newtls     __asm__("r6") = newtls;
823cab2bb3Spatrick 
833cab2bb3Spatrick   __asm__ __volatile__(
843cab2bb3Spatrick                        /* Clone. */
853cab2bb3Spatrick                        "svc    %1\n"
863cab2bb3Spatrick 
873cab2bb3Spatrick                        /* if (%r2 != 0)
883cab2bb3Spatrick                         *   return;
893cab2bb3Spatrick                         */
903cab2bb3Spatrick #ifdef __s390x__
913cab2bb3Spatrick                        "cghi   %%r2, 0\n"
923cab2bb3Spatrick #else
933cab2bb3Spatrick                        "chi    %%r2, 0\n"
943cab2bb3Spatrick #endif
953cab2bb3Spatrick                        "jne    1f\n"
963cab2bb3Spatrick 
973cab2bb3Spatrick                        /* Call "fn(arg)". */
983cab2bb3Spatrick #ifdef __s390x__
993cab2bb3Spatrick                        "lmg    %%r1, %%r2, 8(%%r15)\n"
1003cab2bb3Spatrick #else
1013cab2bb3Spatrick                        "lm     %%r1, %%r2, 4(%%r15)\n"
1023cab2bb3Spatrick #endif
1033cab2bb3Spatrick                        "basr   %%r14, %%r1\n"
1043cab2bb3Spatrick 
1053cab2bb3Spatrick                        /* Call _exit(%r2). */
1063cab2bb3Spatrick                        "svc %2\n"
1073cab2bb3Spatrick 
1083cab2bb3Spatrick                        /* Return to parent. */
1093cab2bb3Spatrick                      "1:\n"
1103cab2bb3Spatrick                        : "=r" (res)
1113cab2bb3Spatrick                        : "i"(__NR_clone), "i"(__NR_exit),
1123cab2bb3Spatrick                          "r"(__cstack),
1133cab2bb3Spatrick                          "r"(__flags),
1143cab2bb3Spatrick                          "r"(__ptidptr),
1153cab2bb3Spatrick                          "r"(__ctidptr),
1163cab2bb3Spatrick                          "r"(__newtls)
1173cab2bb3Spatrick                        : "memory", "cc");
118*810390e3Srobert   if (res >= (uptr)-4095) {
119*810390e3Srobert     errno = -res;
120*810390e3Srobert     return -1;
121*810390e3Srobert   }
1223cab2bb3Spatrick   return res;
1233cab2bb3Spatrick }
1243cab2bb3Spatrick 
1253cab2bb3Spatrick #if SANITIZER_S390_64
FixedCVE_2016_2143()1263cab2bb3Spatrick static bool FixedCVE_2016_2143() {
1273cab2bb3Spatrick   // Try to determine if the running kernel has a fix for CVE-2016-2143,
1283cab2bb3Spatrick   // return false if in doubt (better safe than sorry).  Distros may want to
1293cab2bb3Spatrick   // adjust this for their own kernels.
1303cab2bb3Spatrick   struct utsname buf;
1313cab2bb3Spatrick   unsigned int major, minor, patch = 0;
1323cab2bb3Spatrick   // This should never fail, but just in case...
1331f9cb04fSpatrick   if (internal_uname(&buf))
1343cab2bb3Spatrick     return false;
1353cab2bb3Spatrick   const char *ptr = buf.release;
1363cab2bb3Spatrick   major = internal_simple_strtoll(ptr, &ptr, 10);
1373cab2bb3Spatrick   // At least first 2 should be matched.
1383cab2bb3Spatrick   if (ptr[0] != '.')
1393cab2bb3Spatrick     return false;
1403cab2bb3Spatrick   minor = internal_simple_strtoll(ptr+1, &ptr, 10);
1413cab2bb3Spatrick   // Third is optional.
1423cab2bb3Spatrick   if (ptr[0] == '.')
1433cab2bb3Spatrick     patch = internal_simple_strtoll(ptr+1, &ptr, 10);
1443cab2bb3Spatrick   if (major < 3) {
1453cab2bb3Spatrick     if (major == 2 && minor == 6 && patch == 32 && ptr[0] == '-' &&
1463cab2bb3Spatrick         internal_strstr(ptr, ".el6")) {
1473cab2bb3Spatrick       // Check RHEL6
1483cab2bb3Spatrick       int r1 = internal_simple_strtoll(ptr+1, &ptr, 10);
1493cab2bb3Spatrick       if (r1 >= 657) // 2.6.32-657.el6 or later
1503cab2bb3Spatrick         return true;
1513cab2bb3Spatrick       if (r1 == 642 && ptr[0] == '.') {
1523cab2bb3Spatrick         int r2 = internal_simple_strtoll(ptr+1, &ptr, 10);
1533cab2bb3Spatrick         if (r2 >= 9) // 2.6.32-642.9.1.el6 or later
1543cab2bb3Spatrick           return true;
1553cab2bb3Spatrick       }
1563cab2bb3Spatrick     }
1573cab2bb3Spatrick     // <3.0 is bad.
1583cab2bb3Spatrick     return false;
1593cab2bb3Spatrick   } else if (major == 3) {
1603cab2bb3Spatrick     // 3.2.79+ is OK.
1613cab2bb3Spatrick     if (minor == 2 && patch >= 79)
1623cab2bb3Spatrick       return true;
1633cab2bb3Spatrick     // 3.12.58+ is OK.
1643cab2bb3Spatrick     if (minor == 12 && patch >= 58)
1653cab2bb3Spatrick       return true;
1663cab2bb3Spatrick     if (minor == 10 && patch == 0 && ptr[0] == '-' &&
1673cab2bb3Spatrick         internal_strstr(ptr, ".el7")) {
1683cab2bb3Spatrick       // Check RHEL7
1693cab2bb3Spatrick       int r1 = internal_simple_strtoll(ptr+1, &ptr, 10);
1703cab2bb3Spatrick       if (r1 >= 426) // 3.10.0-426.el7 or later
1713cab2bb3Spatrick         return true;
1723cab2bb3Spatrick       if (r1 == 327 && ptr[0] == '.') {
1733cab2bb3Spatrick         int r2 = internal_simple_strtoll(ptr+1, &ptr, 10);
1743cab2bb3Spatrick         if (r2 >= 27) // 3.10.0-327.27.1.el7 or later
1753cab2bb3Spatrick           return true;
1763cab2bb3Spatrick       }
1773cab2bb3Spatrick     }
1783cab2bb3Spatrick     // Otherwise, bad.
1793cab2bb3Spatrick     return false;
1803cab2bb3Spatrick   } else if (major == 4) {
1813cab2bb3Spatrick     // 4.1.21+ is OK.
1823cab2bb3Spatrick     if (minor == 1 && patch >= 21)
1833cab2bb3Spatrick       return true;
1843cab2bb3Spatrick     // 4.4.6+ is OK.
1853cab2bb3Spatrick     if (minor == 4 && patch >= 6)
1863cab2bb3Spatrick       return true;
1873cab2bb3Spatrick     if (minor == 4 && patch == 0 && ptr[0] == '-' &&
1883cab2bb3Spatrick         internal_strstr(buf.version, "Ubuntu")) {
1893cab2bb3Spatrick       // Check Ubuntu 16.04
1903cab2bb3Spatrick       int r1 = internal_simple_strtoll(ptr+1, &ptr, 10);
1913cab2bb3Spatrick       if (r1 >= 13) // 4.4.0-13 or later
1923cab2bb3Spatrick         return true;
1933cab2bb3Spatrick     }
1943cab2bb3Spatrick     // Otherwise, OK if 4.5+.
1953cab2bb3Spatrick     return minor >= 5;
1963cab2bb3Spatrick   } else {
1973cab2bb3Spatrick     // Linux 5 and up are fine.
1983cab2bb3Spatrick     return true;
1993cab2bb3Spatrick   }
2003cab2bb3Spatrick }
2013cab2bb3Spatrick 
AvoidCVE_2016_2143()2023cab2bb3Spatrick void AvoidCVE_2016_2143() {
2033cab2bb3Spatrick   // Older kernels are affected by CVE-2016-2143 - they will crash hard
2043cab2bb3Spatrick   // if someone uses 4-level page tables (ie. virtual addresses >= 4TB)
2053cab2bb3Spatrick   // and fork() in the same process.  Unfortunately, sanitizers tend to
2063cab2bb3Spatrick   // require such addresses.  Since this is very likely to crash the whole
2073cab2bb3Spatrick   // machine (sanitizers themselves use fork() for llvm-symbolizer, for one),
2083cab2bb3Spatrick   // abort the process at initialization instead.
2093cab2bb3Spatrick   if (FixedCVE_2016_2143())
2103cab2bb3Spatrick     return;
2113cab2bb3Spatrick   if (GetEnv("SANITIZER_IGNORE_CVE_2016_2143"))
2123cab2bb3Spatrick     return;
2133cab2bb3Spatrick   Report(
2143cab2bb3Spatrick     "ERROR: Your kernel seems to be vulnerable to CVE-2016-2143.  Using ASan,\n"
2153cab2bb3Spatrick     "MSan, TSan, DFSan or LSan with such kernel can and will crash your\n"
2163cab2bb3Spatrick     "machine, or worse.\n"
2173cab2bb3Spatrick     "\n"
2183cab2bb3Spatrick     "If you are certain your kernel is not vulnerable (you have compiled it\n"
2193cab2bb3Spatrick     "yourself, or are using an unrecognized distribution kernel), you can\n"
2203cab2bb3Spatrick     "override this safety check by exporting SANITIZER_IGNORE_CVE_2016_2143\n"
2213cab2bb3Spatrick     "with any value.\n");
2223cab2bb3Spatrick   Die();
2233cab2bb3Spatrick }
2243cab2bb3Spatrick #endif
2253cab2bb3Spatrick 
2263cab2bb3Spatrick } // namespace __sanitizer
2273cab2bb3Spatrick 
2283cab2bb3Spatrick #endif // SANITIZER_LINUX && SANITIZER_S390
229