[TSAN] add support for riscv64 (#68735)

Implements for sv39 and sv48 VMA layout.

Userspace only has access to the bottom half of vma range. The top half
is used by kernel. There is no dedicated vsyscall or heap segment.
PIE program is allocated to start at TASK_SIZE/3*2. Maximum ASLR is
ARCH_MMAP_RND_BITS_MAX+PAGE_SHIFT=24+12=36 Loader, vdso and other
libraries are allocated below stack from the top.

Also change RestoreAddr to use 4 bits to accommodate MappingRiscv64_48

Reviewed by: MaskRay, dvyukov, asb, StephenFan, luismarques, jrtc27,
hiraditya, vitalybuka

Differential Revision: https://reviews.llvm.org/D145214

D145214 was reverted because one file was missing in the latest commit.
Luckily the file was there in the previous commit, probably the author
missed uploading that file with latest commit.

Co-authored-by: Alex Fan <alex.fan.q@gmail.com>
This commit is contained in:
AdityaK 2023-10-12 16:03:07 -07:00 committed by GitHub
parent b90fcafcd6
commit 46cb8d9a32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 318 additions and 17 deletions

View File

@ -801,7 +801,7 @@ SanitizerMask Linux::getSupportedSanitizers() const {
IsRISCV64 || IsSystemZ || IsHexagon || IsLoongArch64)
Res |= SanitizerKind::Leak;
if (IsX86_64 || IsMIPS64 || IsAArch64 || IsPowerPC64 || IsSystemZ ||
IsLoongArch64)
IsLoongArch64 || IsRISCV64)
Res |= SanitizerKind::Thread;
if (IsX86_64 || IsSystemZ)
Res |= SanitizerKind::KernelMemory;

View File

@ -66,7 +66,7 @@ set(ALL_PROFILE_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${PPC32} ${PPC
${MIPS32} ${MIPS64} ${S390X} ${SPARC} ${SPARCV9} ${HEXAGON}
${RISCV32} ${RISCV64} ${LOONGARCH64})
set(ALL_TSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${PPC64} ${S390X}
${LOONGARCH64})
${LOONGARCH64} ${RISCV64})
set(ALL_UBSAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${RISCV64}
${MIPS32} ${MIPS64} ${PPC64} ${S390X} ${SPARC} ${SPARCV9} ${HEXAGON}
${LOONGARCH64})

View File

@ -303,7 +303,7 @@
# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 40)
# endif
#elif SANITIZER_RISCV64
# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 38)
# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47)
#elif defined(__aarch64__)
# if SANITIZER_APPLE
# if SANITIZER_OSX || SANITIZER_IOSSIM

View File

@ -220,6 +220,10 @@ else()
set(TSAN_ASM_SOURCES
tsan_rtl_mips64.S
)
elseif(arch MATCHES "riscv64")
set(TSAN_ASM_SOURCES
tsan_rtl_riscv64.S
)
elseif(arch MATCHES "s390x")
set(TSAN_ASM_SOURCES
tsan_rtl_s390x.S

View File

@ -81,6 +81,8 @@ struct ucontext_t {
#define PTHREAD_ABI_BASE "GLIBC_2.17"
#elif SANITIZER_LOONGARCH64
#define PTHREAD_ABI_BASE "GLIBC_2.36"
#elif SANITIZER_RISCV64
# define PTHREAD_ABI_BASE "GLIBC_2.27"
#endif
extern "C" int pthread_attr_init(void *attr);

View File

@ -377,6 +377,71 @@ struct MappingPPC64_47 {
static const uptr kMidAppMemEnd = 0;
};
/*
C/C++ on linux/riscv64 (39-bit VMA)
0000 0010 00 - 0200 0000 00: main binary ( 8 GB)
0200 0000 00 - 1000 0000 00: -
1000 0000 00 - 4000 0000 00: shadow memory (64 GB)
4000 0000 00 - 4800 0000 00: metainfo (16 GB)
4800 0000 00 - 5500 0000 00: -
5500 0000 00 - 5a00 0000 00: main binary (PIE) (~8 GB)
5600 0000 00 - 7c00 0000 00: -
7d00 0000 00 - 7fff ffff ff: libraries and main thread stack ( 8 GB)
mmap by default allocates from top downwards
VDSO sits below loader and above dynamic libraries, within HiApp region.
Heap starts after program region whose position depends on pie or non-pie.
Disable tracking them since their locations are not fixed.
*/
struct MappingRiscv64_39 {
static const uptr kLoAppMemBeg = 0x0000001000ull;
static const uptr kLoAppMemEnd = 0x0200000000ull;
static const uptr kShadowBeg = 0x1000000000ull;
static const uptr kShadowEnd = 0x2000000000ull;
static const uptr kMetaShadowBeg = 0x2000000000ull;
static const uptr kMetaShadowEnd = 0x2400000000ull;
static const uptr kMidAppMemBeg = 0x2aaaaaa000ull;
static const uptr kMidAppMemEnd = 0x2c00000000ull;
static const uptr kHeapMemBeg = 0x2c00000000ull;
static const uptr kHeapMemEnd = 0x2c00000000ull;
static const uptr kHiAppMemBeg = 0x3c00000000ull;
static const uptr kHiAppMemEnd = 0x3fffffffffull;
static const uptr kShadowMsk = 0x3800000000ull;
static const uptr kShadowXor = 0x0800000000ull;
static const uptr kShadowAdd = 0x0000000000ull;
static const uptr kVdsoBeg = 0x4000000000ull;
};
/*
C/C++ on linux/riscv64 (48-bit VMA)
0000 0000 1000 - 0500 0000 0000: main binary ( 5 TB)
0500 0000 0000 - 2000 0000 0000: -
2000 0000 0000 - 4000 0000 0000: shadow memory (32 TB)
4000 0000 0000 - 4800 0000 0000: metainfo ( 8 TB)
4800 0000 0000 - 5555 5555 5000: -
5555 5555 5000 - 5a00 0000 0000: main binary (PIE) (~5 TB)
5a00 0000 0000 - 7a00 0000 0000: -
7a00 0000 0000 - 7fff ffff ffff: libraries and main thread stack ( 5 TB)
*/
struct MappingRiscv64_48 {
static const uptr kLoAppMemBeg = 0x000000001000ull;
static const uptr kLoAppMemEnd = 0x050000000000ull;
static const uptr kShadowBeg = 0x200000000000ull;
static const uptr kShadowEnd = 0x400000000000ull;
static const uptr kMetaShadowBeg = 0x400000000000ull;
static const uptr kMetaShadowEnd = 0x480000000000ull;
static const uptr kMidAppMemBeg = 0x555555555000ull;
static const uptr kMidAppMemEnd = 0x5a0000000000ull;
static const uptr kHeapMemBeg = 0x5a0000000000ull;
static const uptr kHeapMemEnd = 0x5a0000000000ull;
static const uptr kHiAppMemBeg = 0x7a0000000000ull;
static const uptr kHiAppMemEnd = 0x7fffffffffffull;
static const uptr kShadowMsk = 0x700000000000ull;
static const uptr kShadowXor = 0x100000000000ull;
static const uptr kShadowAdd = 0x000000000000ull;
static const uptr kVdsoBeg = 0x800000000000ull;
};
/*
C/C++ on linux/s390x
While the kernel provides a 64-bit address space, we have to restrict ourselves
@ -665,6 +730,13 @@ ALWAYS_INLINE auto SelectMapping(Arg arg) {
}
# elif defined(__mips64)
return Func::template Apply<MappingMips64_40>(arg);
# elif SANITIZER_RISCV64
switch (vmaSize) {
case 39:
return Func::template Apply<MappingRiscv64_39>(arg);
case 48:
return Func::template Apply<MappingRiscv64_48>(arg);
}
# elif defined(__s390x__)
return Func::template Apply<MappingS390x>(arg);
# else
@ -686,6 +758,8 @@ void ForEachMapping() {
Func::template Apply<MappingPPC64_44>();
Func::template Apply<MappingPPC64_46>();
Func::template Apply<MappingPPC64_47>();
Func::template Apply<MappingRiscv64_39>();
Func::template Apply<MappingRiscv64_48>();
Func::template Apply<MappingS390x>();
Func::template Apply<MappingGo48>();
Func::template Apply<MappingGoWindows>();
@ -894,7 +968,7 @@ struct RestoreAddrImpl {
Mapping::kMidAppMemEnd, Mapping::kHiAppMemBeg, Mapping::kHiAppMemEnd,
Mapping::kHeapMemBeg, Mapping::kHeapMemEnd,
};
const uptr indicator = 0x0e0000000000ull;
const uptr indicator = 0x0f0000000000ull;
const uptr ind_lsb = 1ull << LeastSignificantSetBitIndex(indicator);
for (uptr i = 0; i < ARRAY_SIZE(ranges); i += 2) {
uptr beg = ranges[i];

View File

@ -267,7 +267,17 @@ void InitializePlatformEarly() {
Die();
}
# endif
#endif
# elif SANITIZER_RISCV64
// the bottom half of vma is allocated for userspace
vmaSize = vmaSize + 1;
# if !SANITIZER_GO
if (vmaSize != 39 && vmaSize != 48) {
Printf("FATAL: ThreadSanitizer: unsupported VMA range\n");
Printf("FATAL: Found %zd - Supported 39 and 48\n", vmaSize);
Die();
}
# endif
# endif
}
void InitializePlatform() {
@ -399,13 +409,15 @@ static uptr UnmangleLongJmpSp(uptr mangled_sp) {
return mangled_sp ^ xor_key;
#elif defined(__mips__)
return mangled_sp;
#elif defined(__s390x__)
# elif SANITIZER_RISCV64
return mangled_sp;
# elif defined(__s390x__)
// tcbhead_t.stack_guard
uptr xor_key = ((uptr *)__builtin_thread_pointer())[5];
return mangled_sp ^ xor_key;
#else
#error "Unknown platform"
#endif
# else
# error "Unknown platform"
# endif
}
#if SANITIZER_NETBSD
@ -429,11 +441,13 @@ static uptr UnmangleLongJmpSp(uptr mangled_sp) {
# define LONG_JMP_SP_ENV_SLOT 1
# elif defined(__mips64)
# define LONG_JMP_SP_ENV_SLOT 1
# elif defined(__s390x__)
# define LONG_JMP_SP_ENV_SLOT 9
# else
# define LONG_JMP_SP_ENV_SLOT 6
# endif
# elif SANITIZER_RISCV64
# define LONG_JMP_SP_ENV_SLOT 13
# elif defined(__s390x__)
# define LONG_JMP_SP_ENV_SLOT 9
# else
# define LONG_JMP_SP_ENV_SLOT 6
# endif
#endif
uptr ExtractLongJmpSp(uptr *env) {

View File

@ -56,8 +56,8 @@ namespace __tsan {
#if !SANITIZER_GO
struct MapUnmapCallback;
#if defined(__mips64) || defined(__aarch64__) || defined(__loongarch__) || \
defined(__powerpc__)
# if defined(__mips64) || defined(__aarch64__) || defined(__loongarch__) || \
defined(__powerpc__) || SANITIZER_RISCV64
struct AP32 {
static const uptr kSpaceBeg = 0;

View File

@ -0,0 +1,203 @@
#include "sanitizer_common/sanitizer_asm.h"
.section .text
.comm _ZN14__interception11real_setjmpE,8,8
.globl ASM_SYMBOL_INTERCEPTOR(setjmp)
ASM_TYPE_FUNCTION(ASM_SYMBOL_INTERCEPTOR(setjmp))
ASM_SYMBOL_INTERCEPTOR(setjmp):
CFI_STARTPROC
// Save frame pointer and return address register
addi sp, sp, -32
sd ra, 24(sp)
sd s0, 16(sp)
CFI_DEF_CFA_OFFSET (32)
CFI_OFFSET (1, -8)
CFI_OFFSET (8, -16)
// Adjust the SP for previous frame
addi s0, sp, 32
CFI_DEF_CFA_REGISTER (8)
// Save env parameter
sd a0, 8(sp)
CFI_OFFSET (10, -24)
// Obtain SP, first argument to `void __tsan_setjmp(uptr sp)`
addi a0, s0, 0
// call tsan interceptor
call ASM_SYMBOL(__tsan_setjmp)
// Restore env parameter
ld a0, 8(sp)
CFI_RESTORE (10)
// Restore frame/link register
ld s0, 16(sp)
ld ra, 24(sp)
addi sp, sp, 32
CFI_RESTORE (8)
CFI_RESTORE (1)
CFI_DEF_CFA (2, 0)
// tail jump to libc setjmp
la t1, _ZN14__interception11real_setjmpE
ld t1, 0(t1)
jr t1
CFI_ENDPROC
ASM_SIZE(ASM_SYMBOL_INTERCEPTOR(setjmp))
.comm _ZN14__interception12real__setjmpE,8,8
.globl ASM_SYMBOL_INTERCEPTOR(_setjmp)
ASM_TYPE_FUNCTION(ASM_SYMBOL_INTERCEPTOR(_setjmp))
ASM_SYMBOL_INTERCEPTOR(_setjmp):
CFI_STARTPROC
// Save frame pointer and return address register
addi sp, sp, -32
sd ra, 24(sp)
sd s0, 16(sp)
CFI_DEF_CFA_OFFSET (32)
CFI_OFFSET (1, -8)
CFI_OFFSET (8, -16)
// Adjust the SP for previous frame
addi s0, sp, 32
CFI_DEF_CFA_REGISTER (8)
// Save env parameter
sd a0, 8(sp)
CFI_OFFSET (10, -24)
// Obtain SP, first argument to `void __tsan_setjmp(uptr sp)`
addi a0, s0, 0
// call tsan interceptor
call ASM_SYMBOL(__tsan_setjmp)
// Restore env parameter
ld a0, 8(sp)
CFI_RESTORE (10)
// Restore frame/link register
ld s0, 16(sp)
ld ra, 24(sp)
addi sp, sp, 32
CFI_RESTORE (8)
CFI_RESTORE (1)
CFI_DEF_CFA (2, 0)
// tail jump to libc setjmp
la t1, _ZN14__interception12real__setjmpE
ld t1, 0(t1)
jr t1
CFI_ENDPROC
ASM_SIZE(ASM_SYMBOL_INTERCEPTOR(_setjmp))
.comm _ZN14__interception14real_sigsetjmpE,8,8
.globl ASM_SYMBOL_INTERCEPTOR(sigsetjmp)
ASM_TYPE_FUNCTION(ASM_SYMBOL_INTERCEPTOR(sigsetjmp))
ASM_SYMBOL_INTERCEPTOR(sigsetjmp):
CFI_STARTPROC
// Save frame pointer and return address register
addi sp, sp, -32
sd ra, 24(sp)
sd s0, 16(sp)
CFI_DEF_CFA_OFFSET (32)
CFI_OFFSET (1, -8)
CFI_OFFSET (8, -16)
// Adjust the SP for previous frame
addi s0, sp, 32
CFI_DEF_CFA_REGISTER (8)
// Save env parameter
sd a0, 8(sp)
sd a1, 0(sp)
CFI_OFFSET (10, -24)
CFI_OFFSET (11, -32)
// Obtain SP, first argument to `void __tsan_setjmp(uptr sp)`
addi a0, s0, 0
// call tsan interceptor
call ASM_SYMBOL(__tsan_setjmp)
// Restore env parameter
ld a0, 8(sp)
ld a1, 0(sp)
CFI_RESTORE (10)
CFI_RESTORE (11)
// Restore frame/link register
ld s0, 16(sp)
ld ra, 24(sp)
addi sp, sp, 32
CFI_RESTORE (8)
CFI_RESTORE (1)
CFI_DEF_CFA (2, 0)
// tail jump to libc setjmp
la t1, _ZN14__interception14real_sigsetjmpE
ld t1, 0(t1)
jr t1
CFI_ENDPROC
ASM_SIZE(ASM_SYMBOL_INTERCEPTOR(sigsetjmp))
.comm _ZN14__interception16real___sigsetjmpE,8,8
.globl ASM_SYMBOL_INTERCEPTOR(__sigsetjmp)
ASM_TYPE_FUNCTION(ASM_SYMBOL_INTERCEPTOR(__sigsetjmp))
ASM_SYMBOL_INTERCEPTOR(__sigsetjmp):
CFI_STARTPROC
// Save frame pointer and return address register
addi sp, sp, -32
sd ra, 24(sp)
sd s0, 16(sp)
CFI_DEF_CFA_OFFSET (32)
CFI_OFFSET (1, -8)
CFI_OFFSET (8, -16)
// Adjust the SP for previous frame
addi s0, sp, 32
CFI_DEF_CFA_REGISTER (8)
// Save env parameter
sd a0, 8(sp)
sd a1, 0(sp)
CFI_OFFSET (10, -24)
CFI_OFFSET (11, -32)
// Obtain SP, first argument to `void __tsan_setjmp(uptr sp)`
addi a0, s0, 0
// call tsan interceptor
call ASM_SYMBOL(__tsan_setjmp)
// Restore env parameter
ld a0, 8(sp)
ld a1, 0(sp)
CFI_RESTORE (10)
CFI_RESTORE (11)
// Restore frame/link register
ld s0, 16(sp)
ld ra, 24(sp)
addi sp, sp, 32
CFI_RESTORE (8)
CFI_RESTORE (1)
CFI_DEF_CFA (2, 0)
// tail jump to libc setjmp
la t1, _ZN14__interception16real___sigsetjmpE
ld t1, 0(t1)
jr t1
CFI_ENDPROC
ASM_SIZE(ASM_SYMBOL_INTERCEPTOR(__sigsetjmp))

View File

@ -13,6 +13,7 @@
// XFAIL: target=powerpc64{{.*}}
// XFAIL: target=s390x{{.*}}
// XFAIL: target=loongarch64{{.*}}
// XFAIL: target=riscv64{{.*}}
// MAP_32BIT doesn't exist on OS X and NetBSD.
// UNSUPPORTED: darwin,target={{.*netbsd.*}}

View File

@ -17,7 +17,8 @@
int main() {
#ifdef __x86_64__
const size_t kLog2Size = 39;
#elif defined(__mips64) || defined(__aarch64__) || defined(__loongarch_lp64)
#elif defined(__mips64) || defined(__aarch64__) || \
defined(__loongarch_lp64) || (defined(__riscv) && __riscv_xlen == 64)
const size_t kLog2Size = 32;
#elif defined(__powerpc64__)
const size_t kLog2Size = 39;

View File

@ -76,6 +76,8 @@ unsigned long long monotonic_clock_ns() {
const int kPCInc = 1;
#elif defined(__sparc__) || defined(__mips__)
const int kPCInc = 8;
#elif defined(__riscv) && __riscv_xlen == 64
const int kPCInc = 2;
#else
const int kPCInc = 4;
#endif