| //===-- 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 |