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