229 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			229 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===-- sanitizer_linux_s390.cpp ------------------------------------------===//
 | |
| //
 | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | |
| // See https://llvm.org/LICENSE.txt for license information.
 | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| //
 | |
| // This file is shared between AddressSanitizer and ThreadSanitizer
 | |
| // run-time libraries and implements s390-linux-specific functions from
 | |
| // sanitizer_libc.h.
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "sanitizer_platform.h"
 | |
| 
 | |
| #if SANITIZER_LINUX && SANITIZER_S390
 | |
| 
 | |
| #include <dlfcn.h>
 | |
| #include <errno.h>
 | |
| #include <sys/syscall.h>
 | |
| #include <sys/utsname.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| #include "sanitizer_libc.h"
 | |
| #include "sanitizer_linux.h"
 | |
| 
 | |
| namespace __sanitizer {
 | |
| 
 | |
| // --------------- sanitizer_libc.h
 | |
| uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
 | |
|                    u64 offset) {
 | |
|   struct s390_mmap_params {
 | |
|     unsigned long addr;
 | |
|     unsigned long length;
 | |
|     unsigned long prot;
 | |
|     unsigned long flags;
 | |
|     unsigned long fd;
 | |
|     unsigned long offset;
 | |
|   } params = {
 | |
|     (unsigned long)addr,
 | |
|     (unsigned long)length,
 | |
|     (unsigned long)prot,
 | |
|     (unsigned long)flags,
 | |
|     (unsigned long)fd,
 | |
| # ifdef __s390x__
 | |
|     (unsigned long)offset,
 | |
| # else
 | |
|     (unsigned long)(offset / 4096),
 | |
| # endif
 | |
|   };
 | |
| # ifdef __s390x__
 | |
|   return syscall(__NR_mmap, ¶ms);
 | |
| # else
 | |
|   return syscall(__NR_mmap2, ¶ms);
 | |
| # endif
 | |
| }
 | |
| 
 | |
| uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
 | |
|                     int *parent_tidptr, void *newtls, int *child_tidptr) {
 | |
|   if (!fn || !child_stack) {
 | |
|     errno = EINVAL;
 | |
|     return -1;
 | |
|   }
 | |
|   CHECK_EQ(0, (uptr)child_stack % 16);
 | |
|   // Minimum frame size.
 | |
| #ifdef __s390x__
 | |
|   child_stack = (char *)child_stack - 160;
 | |
| #else
 | |
|   child_stack = (char *)child_stack - 96;
 | |
| #endif
 | |
|   // Terminate unwind chain.
 | |
|   ((unsigned long *)child_stack)[0] = 0;
 | |
|   // And pass parameters.
 | |
|   ((unsigned long *)child_stack)[1] = (uptr)fn;
 | |
|   ((unsigned long *)child_stack)[2] = (uptr)arg;
 | |
|   register uptr res __asm__("r2");
 | |
|   register void *__cstack      __asm__("r2") = child_stack;
 | |
|   register long __flags        __asm__("r3") = flags;
 | |
|   register int * __ptidptr     __asm__("r4") = parent_tidptr;
 | |
|   register int * __ctidptr     __asm__("r5") = child_tidptr;
 | |
|   register void * __newtls     __asm__("r6") = newtls;
 | |
| 
 | |
|   __asm__ __volatile__(
 | |
|                        /* Clone. */
 | |
|                        "svc    %1\n"
 | |
| 
 | |
|                        /* if (%r2 != 0)
 | |
|                         *   return;
 | |
|                         */
 | |
| #ifdef __s390x__
 | |
|                        "cghi   %%r2, 0\n"
 | |
| #else
 | |
|                        "chi    %%r2, 0\n"
 | |
| #endif
 | |
|                        "jne    1f\n"
 | |
| 
 | |
|                        /* Call "fn(arg)". */
 | |
| #ifdef __s390x__
 | |
|                        "lmg    %%r1, %%r2, 8(%%r15)\n"
 | |
| #else
 | |
|                        "lm     %%r1, %%r2, 4(%%r15)\n"
 | |
| #endif
 | |
|                        "basr   %%r14, %%r1\n"
 | |
| 
 | |
|                        /* Call _exit(%r2). */
 | |
|                        "svc %2\n"
 | |
| 
 | |
|                        /* Return to parent. */
 | |
|                      "1:\n"
 | |
|                        : "=r" (res)
 | |
|                        : "i"(__NR_clone), "i"(__NR_exit),
 | |
|                          "r"(__cstack),
 | |
|                          "r"(__flags),
 | |
|                          "r"(__ptidptr),
 | |
|                          "r"(__ctidptr),
 | |
|                          "r"(__newtls)
 | |
|                        : "memory", "cc");
 | |
|   if (res >= (uptr)-4095) {
 | |
|     errno = -res;
 | |
|     return -1;
 | |
|   }
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| #if SANITIZER_S390_64
 | |
| static bool FixedCVE_2016_2143() {
 | |
|   // Try to determine if the running kernel has a fix for CVE-2016-2143,
 | |
|   // return false if in doubt (better safe than sorry).  Distros may want to
 | |
|   // adjust this for their own kernels.
 | |
|   struct utsname buf;
 | |
|   unsigned int major, minor, patch = 0;
 | |
|   // This should never fail, but just in case...
 | |
|   if (internal_uname(&buf))
 | |
|     return false;
 | |
|   const char *ptr = buf.release;
 | |
|   major = internal_simple_strtoll(ptr, &ptr, 10);
 | |
|   // At least first 2 should be matched.
 | |
|   if (ptr[0] != '.')
 | |
|     return false;
 | |
|   minor = internal_simple_strtoll(ptr+1, &ptr, 10);
 | |
|   // Third is optional.
 | |
|   if (ptr[0] == '.')
 | |
|     patch = internal_simple_strtoll(ptr+1, &ptr, 10);
 | |
|   if (major < 3) {
 | |
|     if (major == 2 && minor == 6 && patch == 32 && ptr[0] == '-' &&
 | |
|         internal_strstr(ptr, ".el6")) {
 | |
|       // Check RHEL6
 | |
|       int r1 = internal_simple_strtoll(ptr+1, &ptr, 10);
 | |
|       if (r1 >= 657) // 2.6.32-657.el6 or later
 | |
|         return true;
 | |
|       if (r1 == 642 && ptr[0] == '.') {
 | |
|         int r2 = internal_simple_strtoll(ptr+1, &ptr, 10);
 | |
|         if (r2 >= 9) // 2.6.32-642.9.1.el6 or later
 | |
|           return true;
 | |
|       }
 | |
|     }
 | |
|     // <3.0 is bad.
 | |
|     return false;
 | |
|   } else if (major == 3) {
 | |
|     // 3.2.79+ is OK.
 | |
|     if (minor == 2 && patch >= 79)
 | |
|       return true;
 | |
|     // 3.12.58+ is OK.
 | |
|     if (minor == 12 && patch >= 58)
 | |
|       return true;
 | |
|     if (minor == 10 && patch == 0 && ptr[0] == '-' &&
 | |
|         internal_strstr(ptr, ".el7")) {
 | |
|       // Check RHEL7
 | |
|       int r1 = internal_simple_strtoll(ptr+1, &ptr, 10);
 | |
|       if (r1 >= 426) // 3.10.0-426.el7 or later
 | |
|         return true;
 | |
|       if (r1 == 327 && ptr[0] == '.') {
 | |
|         int r2 = internal_simple_strtoll(ptr+1, &ptr, 10);
 | |
|         if (r2 >= 27) // 3.10.0-327.27.1.el7 or later
 | |
|           return true;
 | |
|       }
 | |
|     }
 | |
|     // Otherwise, bad.
 | |
|     return false;
 | |
|   } else if (major == 4) {
 | |
|     // 4.1.21+ is OK.
 | |
|     if (minor == 1 && patch >= 21)
 | |
|       return true;
 | |
|     // 4.4.6+ is OK.
 | |
|     if (minor == 4 && patch >= 6)
 | |
|       return true;
 | |
|     if (minor == 4 && patch == 0 && ptr[0] == '-' &&
 | |
|         internal_strstr(buf.version, "Ubuntu")) {
 | |
|       // Check Ubuntu 16.04
 | |
|       int r1 = internal_simple_strtoll(ptr+1, &ptr, 10);
 | |
|       if (r1 >= 13) // 4.4.0-13 or later
 | |
|         return true;
 | |
|     }
 | |
|     // Otherwise, OK if 4.5+.
 | |
|     return minor >= 5;
 | |
|   } else {
 | |
|     // Linux 5 and up are fine.
 | |
|     return true;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void AvoidCVE_2016_2143() {
 | |
|   // Older kernels are affected by CVE-2016-2143 - they will crash hard
 | |
|   // if someone uses 4-level page tables (ie. virtual addresses >= 4TB)
 | |
|   // and fork() in the same process.  Unfortunately, sanitizers tend to
 | |
|   // require such addresses.  Since this is very likely to crash the whole
 | |
|   // machine (sanitizers themselves use fork() for llvm-symbolizer, for one),
 | |
|   // abort the process at initialization instead.
 | |
|   if (FixedCVE_2016_2143())
 | |
|     return;
 | |
|   if (GetEnv("SANITIZER_IGNORE_CVE_2016_2143"))
 | |
|     return;
 | |
|   Report(
 | |
|     "ERROR: Your kernel seems to be vulnerable to CVE-2016-2143.  Using ASan,\n"
 | |
|     "MSan, TSan, DFSan or LSan with such kernel can and will crash your\n"
 | |
|     "machine, or worse.\n"
 | |
|     "\n"
 | |
|     "If you are certain your kernel is not vulnerable (you have compiled it\n"
 | |
|     "yourself, or are using an unrecognized distribution kernel), you can\n"
 | |
|     "override this safety check by exporting SANITIZER_IGNORE_CVE_2016_2143\n"
 | |
|     "with any value.\n");
 | |
|   Die();
 | |
| }
 | |
| #endif
 | |
| 
 | |
| } // namespace __sanitizer
 | |
| 
 | |
| #endif // SANITIZER_LINUX && SANITIZER_S390
 |