forked from OSchip/llvm-project
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
|