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 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, ¶ms); 5068d75effSDimitry Andric # else 5168d75effSDimitry Andric return syscall(__NR_mmap2, ¶ms); 5268d75effSDimitry Andric # endif 5368d75effSDimitry Andric } 5468d75effSDimitry Andric 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 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 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