Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion clang/lib/Driver/ToolChains/Linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -819,7 +819,7 @@ SanitizerMask Linux::getSupportedSanitizers() const {
Res |= SanitizerKind::KernelAddress;
Res |= SanitizerKind::Vptr;
Res |= SanitizerKind::SafeStack;
if (IsX86_64 || IsMIPS64 || IsAArch64 || IsLoongArch64)
if (IsX86_64 || IsMIPS64 || IsAArch64 || IsLoongArch64 || IsSystemZ)
Res |= SanitizerKind::DataFlow;
if (IsX86_64 || IsMIPS64 || IsAArch64 || IsX86 || IsArmArch || IsPowerPC64 ||
IsRISCV64 || IsSystemZ || IsHexagon || IsLoongArch64)
Expand Down
3 changes: 2 additions & 1 deletion compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ set(ALL_ASAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${RISCV64}
${MIPS32} ${MIPS64} ${PPC64} ${S390X} ${SPARC} ${SPARCV9} ${HEXAGON}
${LOONGARCH64})
set(ALL_ASAN_ABI_SUPPORTED_ARCH ${X86_64} ${ARM64} ${ARM64_32})
set(ALL_DFSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${LOONGARCH64})
set(ALL_DFSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${LOONGARCH64}
${S390X})
set(ALL_RTSAN_SUPPORTED_ARCH ${X86_64} ${ARM64})

if(ANDROID)
Expand Down
16 changes: 16 additions & 0 deletions compiler-rt/lib/dfsan/dfsan_allocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,24 @@ struct DFsanMapUnmapCallback {
// duplicated as MappingDesc::ALLOCATOR in dfsan_platform.h.
#if defined(__aarch64__)
const uptr kAllocatorSpace = 0xE00000000000ULL;
#elif defined(__s390x__)
const uptr kAllocatorSpace = 0x440000000000ULL;
#else
const uptr kAllocatorSpace = 0x700000000000ULL;
#endif
#if defined(__s390x__)
const uptr kMaxAllowedMallocSize = 2UL << 30; // 2G

struct AP64 { // Allocator64 parameters. Deliberately using a short name.
static const uptr kSpaceBeg = kAllocatorSpace;
static const uptr kSpaceSize = 0x020000000000; // 2T.
static const uptr kMetadataSize = sizeof(Metadata);
using SizeClassMap = DefaultSizeClassMap;
using MapUnmapCallback = DFsanMapUnmapCallback;
static const uptr kFlags = 0;
using AddressSpaceView = LocalAddressSpaceView;
};
#else
Copy link
Contributor

@thurstond thurstond Oct 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the allocator and shadow constants are the tried-and-tested constants from MSan (i.e. those don't need thorough review)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's right. All memory map parameters are taken from tested MSan.

const uptr kMaxAllowedMallocSize = 1ULL << 40;

struct AP64 { // Allocator64 parameters. Deliberately using a short name.
Expand All @@ -59,6 +74,7 @@ struct AP64 { // Allocator64 parameters. Deliberately using a short name.
using AddressSpaceView = LocalAddressSpaceView;
};

#endif
typedef SizeClassAllocator64<AP64> PrimaryAllocator;

typedef CombinedAllocator<PrimaryAllocator> Allocator;
Expand Down
13 changes: 13 additions & 0 deletions compiler-rt/lib/dfsan/dfsan_custom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2332,7 +2332,20 @@ static int format_buffer(char *str, size_t size, const char *fmt,
case 'g':
case 'G':
if (*(formatter.fmt_cur - 1) == 'L') {
#if defined(__s390x__)
// SystemZ treats float128 argument as an aggregate type and copies
// shadow and Origin to passed argument temporary. But passed
// argument va_labels and va_origins are zero. Here. we get
// Shadow/Origin corresponding to in-memory argument and update
// va_labels and va_origins.
long double* arg = va_arg(ap, long double*);
*va_labels = *shadow_for(arg);
if (va_origins != nullptr)
*va_origins = *origin_for(arg);
retval = formatter.format(*arg);
#else
retval = formatter.format(va_arg(ap, long double));
#endif
} else {
retval = formatter.format(va_arg(ap, double));
}
Expand Down
14 changes: 14 additions & 0 deletions compiler-rt/lib/dfsan/dfsan_platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,20 @@ const MappingDesc kMemoryLayout[] = {
};
# define MEM_TO_SHADOW(mem) ((uptr)mem ^ 0xB00000000000ULL)
# define SHADOW_TO_ORIGIN(shadow) (((uptr)(shadow)) + 0x200000000000ULL)
# elif SANITIZER_LINUX && SANITIZER_S390_64
const MappingDesc kMemoryLayout[] = {
{0x000000000000ULL, 0x040000000000ULL, MappingDesc::APP, "low memory"},
{0x040000000000ULL, 0x080000000000ULL, MappingDesc::INVALID, "invalid"},
{0x080000000000ULL, 0x180000000000ULL, MappingDesc::SHADOW, "shadow"},
{0x180000000000ULL, 0x1C0000000000ULL, MappingDesc::INVALID, "invalid"},
{0x1C0000000000ULL, 0x2C0000000000ULL, MappingDesc::ORIGIN, "origin"},
{0x2C0000000000ULL, 0x440000000000ULL, MappingDesc::INVALID, "invalid"},
{0x440000000000ULL, 0x460000000000ULL, MappingDesc::ALLOCATOR, "allocator"},
{0x460000000000ULL, 0x500000000000ULL, MappingDesc::APP, "high memory"}};

# define MEM_TO_SHADOW(mem) \
((((uptr)(mem)) & ~0xC00000000000ULL) + 0x080000000000ULL)
# define SHADOW_TO_ORIGIN(shadow) (((uptr)(shadow)) + 0x140000000000ULL)

# else
// All of the following configurations are supported.
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/test/dfsan/custom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2240,7 +2240,7 @@ void test_sscanf() {
strcpy(input_buf, "-559038737");
test_sscanf_chunk(-559038737, "%d", input_ptr, 1);
strcpy(input_buf, "3735928559");
test_sscanf_chunk(3735928559, "%u", input_ptr, 1);
test_sscanf_chunk(3735928559, "%lu", input_ptr, 1);
strcpy(input_buf, "12345");
test_sscanf_chunk(12345, "%i", input_ptr, 1);
strcpy(input_buf, "0751");
Expand Down
7 changes: 6 additions & 1 deletion compiler-rt/test/dfsan/lit.cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

# Setup default compiler flags used with -fsanitize=dataflow option.
clang_dfsan_cflags = ["-fsanitize=dataflow"] + [config.target_cflags]
if config.target_arch == "s390x":
clang_dfsan_cflags.append("-mbackchain")

clang_dfsan_cxxflags = config.cxx_mode_flags + clang_dfsan_cflags

Expand All @@ -25,5 +27,8 @@ def build_invocation(compile_flags):
config.suffixes = [".c", ".cpp"]

# DataFlowSanitizer tests are currently supported on Linux only.
if not (config.target_os in ["Linux"] and config.target_arch in ["aarch64", "x86_64", "loongarch64"]):
if not (
config.target_os in ["Linux"]
and config.target_arch in ["aarch64", "x86_64", "loongarch64", "s390x"]
):
config.unsupported = True
36 changes: 24 additions & 12 deletions compiler-rt/test/dfsan/pair.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,10 @@ void test_simple_constructors() {
int *ptr1 = pair1.first;

#ifdef O0
assert(dfsan_read_label(&i1, sizeof(i1)) == 10);
assert(dfsan_read_label(&ptr1, sizeof(ptr1)) == 10);
assert(dfsan_read_label(&i1, sizeof(i1)) == 8 ||
dfsan_read_label(&i1, sizeof(i1)) == 10);
assert(dfsan_read_label(&ptr1, sizeof(ptr1)) == 2 ||
dfsan_read_label(&ptr1, sizeof(ptr1)) == 10);
#else
assert(dfsan_read_label(&i1, sizeof(i1)) == 8);
assert(dfsan_read_label(&ptr1, sizeof(ptr1)) == 2);
Expand All @@ -78,8 +80,10 @@ void test_simple_constructors() {
int *ptr2 = pair2.first;

#ifdef O0
assert(dfsan_read_label(&i2, sizeof(i2)) == 10);
assert(dfsan_read_label(&ptr2, sizeof(ptr2)) == 10);
assert(dfsan_read_label(&i2, sizeof(i2)) == 8 ||
dfsan_read_label(&i2, sizeof(i2)) == 10);
assert(dfsan_read_label(&ptr2, sizeof(ptr2)) == 2 ||
dfsan_read_label(&ptr2, sizeof(ptr2)) == 10);
#else
assert(dfsan_read_label(&i2, sizeof(i2)) == 8);
assert(dfsan_read_label(&ptr2, sizeof(ptr2)) == 2);
Expand All @@ -90,8 +94,10 @@ void test_simple_constructors() {
int *ptr3 = pair3.first;

#ifdef O0
assert(dfsan_read_label(&i3, sizeof(i3)) == 10);
assert(dfsan_read_label(&ptr3, sizeof(ptr3)) == 10);
assert(dfsan_read_label(&i3, sizeof(i3)) == 8 ||
dfsan_read_label(&i3, sizeof(i3)) == 10);
assert(dfsan_read_label(&ptr3, sizeof(ptr3)) == 2 ||
dfsan_read_label(&ptr3, sizeof(ptr3)) == 10);
#else
assert(dfsan_read_label(&i3, sizeof(i3)) == 8);
assert(dfsan_read_label(&ptr3, sizeof(ptr3)) == 2);
Expand All @@ -102,8 +108,10 @@ void test_simple_constructors() {
int *ptr4 = pair4.first;

#ifdef O0
assert(dfsan_read_label(&i4, sizeof(i4)) == 10);
assert(dfsan_read_label(&ptr4, sizeof(ptr4)) == 10);
assert(dfsan_read_label(&i4, sizeof(i4)) == 8 ||
dfsan_read_label(&i4, sizeof(i4)) == 10);
assert(dfsan_read_label(&ptr4, sizeof(ptr4)) == 2 ||
dfsan_read_label(&ptr4, sizeof(ptr4)) == 10);
#else
assert(dfsan_read_label(&i4, sizeof(i4)) == 8);
assert(dfsan_read_label(&ptr4, sizeof(ptr4)) == 2);
Expand Down Expand Up @@ -140,8 +148,10 @@ void test_branches() {
{
std::pair<const char *, uint32_t> r = return_ptr_and_i32(q, res);
#ifdef O0
assert(dfsan_read_label(&r.first, sizeof(r.first)) == 10);
assert(dfsan_read_label(&r.second, sizeof(r.second)) == 10);
assert(dfsan_read_label(&r.first, sizeof(r.first)) == 2 ||
dfsan_read_label(&r.first, sizeof(r.first)) == 10);
assert(dfsan_read_label(&r.second, sizeof(r.second)) == 8 ||
dfsan_read_label(&r.second, sizeof(r.second)) == 10);
#else
assert(dfsan_read_label(&r.first, sizeof(r.first)) == 2);
assert(dfsan_read_label(&r.second, sizeof(r.second)) == 8);
Expand All @@ -151,8 +161,10 @@ void test_branches() {
{
std::pair<const char *, uint64_t> r = return_ptr_and_i64(q, res);
#ifdef O0
assert(dfsan_read_label(&r.first, sizeof(r.first)) == 10);
assert(dfsan_read_label(&r.second, sizeof(r.second)) == 10);
assert(dfsan_read_label(&r.first, sizeof(r.first)) == 2 ||
dfsan_read_label(&r.first, sizeof(r.first)) == 10);
assert(dfsan_read_label(&r.second, sizeof(r.second)) == 8 ||
dfsan_read_label(&r.second, sizeof(r.second)) == 10);
#else
assert(dfsan_read_label(&r.first, sizeof(r.first)) == 2);
assert(dfsan_read_label(&r.second, sizeof(r.second)) == 8);
Expand Down
12 changes: 6 additions & 6 deletions compiler-rt/test/dfsan/struct.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ int main(void) {
dfsan_label i1_label = dfsan_read_label(&i1, sizeof(i1));
dfsan_label ptr1_label = dfsan_read_label(&ptr1, sizeof(ptr1));
#if defined(O0)
assert(i1_label == (i_label | ptr_label));
assert(ptr1_label == (i_label | ptr_label));
assert(i1_label == i_label || i1_label == (i_label | ptr_label));
assert(ptr1_label == ptr_label || ptr1_label == (i_label | ptr_label));
#else
assert(i1_label == i_label);
assert(ptr1_label == ptr_label);
Expand All @@ -62,8 +62,8 @@ int main(void) {
dfsan_label i2_label = dfsan_read_label(&i2, sizeof(i2));
dfsan_label ptr2_label = dfsan_read_label(&ptr2, sizeof(ptr2));
#if defined(O0)
assert(i2_label == (i_label | ptr_label));
assert(ptr2_label == (i_label | ptr_label));
assert(i2_label == i_label || i2_label == (i_label | ptr_label));
assert(ptr2_label == ptr_label || ptr2_label == (i_label | ptr_label));
#else
assert(i2_label == i_label);
assert(ptr2_label == ptr_label);
Expand All @@ -76,8 +76,8 @@ int main(void) {
dfsan_label i3_label = dfsan_read_label(&i3, sizeof(i3));
dfsan_label ptr3_label = dfsan_read_label(&ptr3, sizeof(ptr3));
#if defined(O0)
assert(i3_label == (i_label | ptr_label));
assert(ptr3_label == (i_label | ptr_label));
assert(i3_label == i_label || i3_label == (i_label | ptr_label));
assert(ptr3_label == ptr_label || ptr3_label == (i_label | ptr_label));
#else
assert(i3_label == i_label);
assert(ptr3_label == ptr_label);
Expand Down
29 changes: 25 additions & 4 deletions llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,14 @@ const MemoryMapParams Linux_LoongArch64_MemoryMapParams = {
0x100000000000, // OriginBase
};

// s390x Linux
const MemoryMapParams Linux_S390X_MemoryMapParams = {
0xC00000000000, // AndMask
0, // XorMask (not used)
0x080000000000, // ShadowBase
0x1C0000000000, // OriginBase
};

namespace {

class DFSanABIList {
Expand Down Expand Up @@ -1141,6 +1149,9 @@ bool DataFlowSanitizer::initializeModule(Module &M) {
case Triple::loongarch64:
MapParams = &Linux_LoongArch64_MemoryMapParams;
break;
case Triple::systemz:
MapParams = &Linux_S390X_MemoryMapParams;
break;
default:
report_fatal_error("unsupported architecture");
}
Expand Down Expand Up @@ -1951,8 +1962,12 @@ Value *DataFlowSanitizer::getShadowAddress(Value *Addr,
Value *DataFlowSanitizer::getShadowAddress(Value *Addr,
BasicBlock::iterator Pos) {
IRBuilder<> IRB(Pos->getParent(), Pos);
Value *ShadowOffset = getShadowOffset(Addr, IRB);
return getShadowAddress(Addr, Pos, ShadowOffset);
Value *ShadowAddr = getShadowOffset(Addr, IRB);
uint64_t ShadowBase = MapParams->ShadowBase;
if (ShadowBase != 0)
ShadowAddr =
IRB.CreateAdd(ShadowAddr, ConstantInt::get(IntptrTy, ShadowBase));
return getShadowAddress(Addr, Pos, ShadowAddr);
}

Value *DFSanFunction::combineShadowsThenConvert(Type *T, Value *V1, Value *V2,
Expand Down Expand Up @@ -2181,8 +2196,14 @@ std::pair<Value *, Value *> DFSanFunction::loadShadowFast(
// and then the entire shadow for the second origin pointer (which will be
// chosen by combineOrigins() iff the least-significant half of the wide
// shadow was empty but the other half was not).
Value *WideShadowLo = IRB.CreateShl(
WideShadow, ConstantInt::get(WideShadowTy, WideShadowBitWidth / 2));
Value *WideShadowLo =
F->getParent()->getDataLayout().isLittleEndian()
? IRB.CreateShl(
WideShadow,
ConstantInt::get(WideShadowTy, WideShadowBitWidth / 2))
: IRB.CreateAnd(
WideShadow,
ConstantInt::get(WideShadowTy, 0xFFFFFFFF00000000ULL));
Shadows.push_back(WideShadow);
Origins.push_back(DFS.loadNextOrigin(Pos, OriginAlign, &OriginAddr));

Expand Down