Skip to content

Commit c88fede

Browse files
[dfsan] Conservative solution to atomic load/store
DFSan at store does store shadow data; store app data; and at load does load shadow data; load app data. When an application data is atomic, one overtainting case is thread A: load shadow thread B: store shadow thread B: store app thread A: load app If the application address had been used by other flows, thread A reads previous shadow, causing overtainting. The change is similar to MSan's solution. 1) enforce ordering of app load/store 2) load shadow after load app; store shadow before shadow app 3) do not track atomic store by reseting its shadow to be 0. The last one is to address a case like this. Thread A: load app Thread B: store shadow Thread A: load shadow Thread B: store app This approach eliminates overtainting as a trade-off between undertainting flows via shadow data race. Note that this change addresses only native atomic instructions, but does not support builtin libcalls yet. https://llvm.org/docs/Atomics.html#libcalls-atomic Reviewed-by: morehouse Differential Revision: https://reviews.llvm.org/D97310
1 parent 41751b6 commit c88fede

File tree

3 files changed

+490
-14
lines changed

3 files changed

+490
-14
lines changed

compiler-rt/test/dfsan/atomic.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: %clangxx_dfsan %s -fno-exceptions -o %t && %run %t
2+
// RUN: %clangxx_dfsan -mllvm -dfsan-track-origins=1 -mllvm -dfsan-fast-16-labels=true %s -fno-exceptions -o %t && %run %t
3+
//
4+
// Use -fno-exceptions to turn off exceptions to avoid instrumenting
5+
// __cxa_begin_catch, std::terminate and __gxx_personality_v0.
6+
//
7+
// TODO: Support builtin atomics. For example, https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html
8+
// DFSan instrumentation pass cannot identify builtin callsites yet.
9+
10+
#include <sanitizer/dfsan_interface.h>
11+
12+
#include <assert.h>
13+
#include <atomic>
14+
#include <pthread.h>
15+
16+
std::atomic<int> atomic_i{0};
17+
18+
static void *ThreadFn(void *arg) {
19+
if ((size_t)arg % 2) {
20+
int i = 10;
21+
dfsan_set_label(8, (void *)&i, sizeof(i));
22+
atomic_i.store(i, std::memory_order_relaxed);
23+
24+
return 0;
25+
}
26+
int j = atomic_i.load();
27+
assert(dfsan_get_label(j) == 0 || dfsan_get_label(j) == 2);
28+
29+
return 0;
30+
}
31+
32+
int main(void) {
33+
int i = 10;
34+
dfsan_set_label(2, (void *)&i, sizeof(i));
35+
atomic_i.store(i, std::memory_order_relaxed);
36+
const int kNumThreads = 24;
37+
pthread_t t[kNumThreads];
38+
for (int i = 0; i < kNumThreads; ++i) {
39+
pthread_create(&t[i], 0, ThreadFn, (void *)i);
40+
}
41+
for (int i = 0; i < kNumThreads; ++i) {
42+
pthread_join(t[i], 0);
43+
}
44+
return 0;
45+
}

llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp

Lines changed: 122 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,11 @@ struct DFSanFunction {
593593
/// CTP(other types, PS) = PS
594594
Value *collapseToPrimitiveShadow(Value *Shadow, Instruction *Pos);
595595

596+
void storeZeroPrimitiveShadow(Value *Addr, uint64_t Size, Align ShadowAlign,
597+
Instruction *Pos);
598+
599+
Align getShadowAlign(Align InstAlignment);
600+
596601
private:
597602
/// Collapses the shadow with aggregate type into a single primitive shadow
598603
/// value.
@@ -634,6 +639,8 @@ class DFSanVisitor : public InstVisitor<DFSanVisitor> {
634639
void visitGetElementPtrInst(GetElementPtrInst &GEPI);
635640
void visitLoadInst(LoadInst &LI);
636641
void visitStoreInst(StoreInst &SI);
642+
void visitAtomicRMWInst(AtomicRMWInst &I);
643+
void visitAtomicCmpXchgInst(AtomicCmpXchgInst &I);
637644
void visitReturnInst(ReturnInst &RI);
638645
void visitCallBase(CallBase &CB);
639646
void visitPHINode(PHINode &PN);
@@ -648,6 +655,8 @@ class DFSanVisitor : public InstVisitor<DFSanVisitor> {
648655
void visitMemTransferInst(MemTransferInst &I);
649656

650657
private:
658+
void visitCASOrRMW(Align InstAlignment, Instruction &I);
659+
651660
// Returns false when this is an invoke of a custom function.
652661
bool visitWrappedCallBase(Function &F, CallBase &CB);
653662

@@ -1802,6 +1811,11 @@ void DFSanVisitor::visitInstOperandOrigins(Instruction &I) {
18021811
DFSF.setOrigin(&I, CombinedOrigin);
18031812
}
18041813

1814+
Align DFSanFunction::getShadowAlign(Align InstAlignment) {
1815+
const Align Alignment = ClPreserveAlignment ? InstAlignment : Align(1);
1816+
return Align(Alignment.value() * DFS.ShadowWidthBytes);
1817+
}
1818+
18051819
Value *DFSanFunction::loadFast16ShadowFast(Value *ShadowAddr, uint64_t Size,
18061820
Align ShadowAlign,
18071821
Instruction *Pos) {
@@ -1959,6 +1973,23 @@ Value *DFSanFunction::loadShadow(Value *Addr, uint64_t Size, uint64_t Align,
19591973
return FallbackCall;
19601974
}
19611975

1976+
static AtomicOrdering addAcquireOrdering(AtomicOrdering AO) {
1977+
switch (AO) {
1978+
case AtomicOrdering::NotAtomic:
1979+
return AtomicOrdering::NotAtomic;
1980+
case AtomicOrdering::Unordered:
1981+
case AtomicOrdering::Monotonic:
1982+
case AtomicOrdering::Acquire:
1983+
return AtomicOrdering::Acquire;
1984+
case AtomicOrdering::Release:
1985+
case AtomicOrdering::AcquireRelease:
1986+
return AtomicOrdering::AcquireRelease;
1987+
case AtomicOrdering::SequentiallyConsistent:
1988+
return AtomicOrdering::SequentiallyConsistent;
1989+
}
1990+
llvm_unreachable("Unknown ordering");
1991+
}
1992+
19621993
void DFSanVisitor::visitLoadInst(LoadInst &LI) {
19631994
auto &DL = LI.getModule()->getDataLayout();
19641995
uint64_t Size = DL.getTypeStoreSize(LI.getType());
@@ -1967,26 +1998,49 @@ void DFSanVisitor::visitLoadInst(LoadInst &LI) {
19671998
return;
19681999
}
19692000

2001+
// When an application load is atomic, increase atomic ordering between
2002+
// atomic application loads and stores to ensure happen-before order; load
2003+
// shadow data after application data; store zero shadow data before
2004+
// application data. This ensure shadow loads return either labels of the
2005+
// initial application data or zeros.
2006+
if (LI.isAtomic())
2007+
LI.setOrdering(addAcquireOrdering(LI.getOrdering()));
2008+
19702009
Align Alignment = ClPreserveAlignment ? LI.getAlign() : Align(1);
2010+
Instruction *Pos = LI.isAtomic() ? LI.getNextNode() : &LI;
19712011
Value *PrimitiveShadow =
1972-
DFSF.loadShadow(LI.getPointerOperand(), Size, Alignment.value(), &LI);
2012+
DFSF.loadShadow(LI.getPointerOperand(), Size, Alignment.value(), Pos);
19732013
if (ClCombinePointerLabelsOnLoad) {
19742014
Value *PtrShadow = DFSF.getShadow(LI.getPointerOperand());
1975-
PrimitiveShadow = DFSF.combineShadows(PrimitiveShadow, PtrShadow, &LI);
2015+
PrimitiveShadow = DFSF.combineShadows(PrimitiveShadow, PtrShadow, Pos);
19762016
}
19772017
if (!DFSF.DFS.isZeroShadow(PrimitiveShadow))
19782018
DFSF.NonZeroChecks.push_back(PrimitiveShadow);
19792019

19802020
Value *Shadow =
1981-
DFSF.expandFromPrimitiveShadow(LI.getType(), PrimitiveShadow, &LI);
2021+
DFSF.expandFromPrimitiveShadow(LI.getType(), PrimitiveShadow, Pos);
19822022
DFSF.setShadow(&LI, Shadow);
19832023
if (ClEventCallbacks) {
1984-
IRBuilder<> IRB(&LI);
2024+
IRBuilder<> IRB(Pos);
19852025
Value *Addr8 = IRB.CreateBitCast(LI.getPointerOperand(), DFSF.DFS.Int8Ptr);
19862026
IRB.CreateCall(DFSF.DFS.DFSanLoadCallbackFn, {PrimitiveShadow, Addr8});
19872027
}
19882028
}
19892029

2030+
void DFSanFunction::storeZeroPrimitiveShadow(Value *Addr, uint64_t Size,
2031+
Align ShadowAlign,
2032+
Instruction *Pos) {
2033+
IRBuilder<> IRB(Pos);
2034+
IntegerType *ShadowTy =
2035+
IntegerType::get(*DFS.Ctx, Size * DFS.ShadowWidthBits);
2036+
Value *ExtZeroShadow = ConstantInt::get(ShadowTy, 0);
2037+
Value *ShadowAddr = DFS.getShadowAddress(Addr, Pos);
2038+
Value *ExtShadowAddr =
2039+
IRB.CreateBitCast(ShadowAddr, PointerType::getUnqual(ShadowTy));
2040+
IRB.CreateAlignedStore(ExtZeroShadow, ExtShadowAddr, ShadowAlign);
2041+
// Do not write origins for 0 shadows because we do not trace origins for
2042+
// untainted sinks.
2043+
}
19902044
void DFSanFunction::storePrimitiveShadow(Value *Addr, uint64_t Size,
19912045
Align Alignment,
19922046
Value *PrimitiveShadow,
@@ -2001,18 +2055,13 @@ void DFSanFunction::storePrimitiveShadow(Value *Addr, uint64_t Size,
20012055
}
20022056

20032057
const Align ShadowAlign(Alignment.value() * DFS.ShadowWidthBytes);
2004-
IRBuilder<> IRB(Pos);
2005-
Value *ShadowAddr = DFS.getShadowAddress(Addr, Pos);
20062058
if (DFS.isZeroShadow(PrimitiveShadow)) {
2007-
IntegerType *ShadowTy =
2008-
IntegerType::get(*DFS.Ctx, Size * DFS.ShadowWidthBits);
2009-
Value *ExtZeroShadow = ConstantInt::get(ShadowTy, 0);
2010-
Value *ExtShadowAddr =
2011-
IRB.CreateBitCast(ShadowAddr, PointerType::getUnqual(ShadowTy));
2012-
IRB.CreateAlignedStore(ExtZeroShadow, ExtShadowAddr, ShadowAlign);
2059+
storeZeroPrimitiveShadow(Addr, Size, ShadowAlign, Pos);
20132060
return;
20142061
}
20152062

2063+
IRBuilder<> IRB(Pos);
2064+
Value *ShadowAddr = DFS.getShadowAddress(Addr, Pos);
20162065
const unsigned ShadowVecSize = 128 / DFS.ShadowWidthBits;
20172066
uint64_t Offset = 0;
20182067
if (Size >= ShadowVecSize) {
@@ -2044,15 +2093,42 @@ void DFSanFunction::storePrimitiveShadow(Value *Addr, uint64_t Size,
20442093
}
20452094
}
20462095

2096+
static AtomicOrdering addReleaseOrdering(AtomicOrdering AO) {
2097+
switch (AO) {
2098+
case AtomicOrdering::NotAtomic:
2099+
return AtomicOrdering::NotAtomic;
2100+
case AtomicOrdering::Unordered:
2101+
case AtomicOrdering::Monotonic:
2102+
case AtomicOrdering::Release:
2103+
return AtomicOrdering::Release;
2104+
case AtomicOrdering::Acquire:
2105+
case AtomicOrdering::AcquireRelease:
2106+
return AtomicOrdering::AcquireRelease;
2107+
case AtomicOrdering::SequentiallyConsistent:
2108+
return AtomicOrdering::SequentiallyConsistent;
2109+
}
2110+
llvm_unreachable("Unknown ordering");
2111+
}
2112+
20472113
void DFSanVisitor::visitStoreInst(StoreInst &SI) {
20482114
auto &DL = SI.getModule()->getDataLayout();
2049-
uint64_t Size = DL.getTypeStoreSize(SI.getValueOperand()->getType());
2115+
Value *Val = SI.getValueOperand();
2116+
uint64_t Size = DL.getTypeStoreSize(Val->getType());
20502117
if (Size == 0)
20512118
return;
20522119

2120+
// When an application store is atomic, increase atomic ordering between
2121+
// atomic application loads and stores to ensure happen-before order; load
2122+
// shadow data after application data; store zero shadow data before
2123+
// application data. This ensure shadow loads return either labels of the
2124+
// initial application data or zeros.
2125+
if (SI.isAtomic())
2126+
SI.setOrdering(addReleaseOrdering(SI.getOrdering()));
2127+
20532128
const Align Alignment = ClPreserveAlignment ? SI.getAlign() : Align(1);
20542129

2055-
Value* Shadow = DFSF.getShadow(SI.getValueOperand());
2130+
Value *Shadow =
2131+
SI.isAtomic() ? DFSF.DFS.getZeroShadow(Val) : DFSF.getShadow(Val);
20562132
Value *PrimitiveShadow;
20572133
if (ClCombinePointerLabelsOnStore) {
20582134
Value *PtrShadow = DFSF.getShadow(SI.getPointerOperand());
@@ -2069,6 +2145,38 @@ void DFSanVisitor::visitStoreInst(StoreInst &SI) {
20692145
}
20702146
}
20712147

2148+
void DFSanVisitor::visitCASOrRMW(Align InstAlignment, Instruction &I) {
2149+
assert(isa<AtomicRMWInst>(I) || isa<AtomicCmpXchgInst>(I));
2150+
2151+
Value *Val = I.getOperand(1);
2152+
const auto &DL = I.getModule()->getDataLayout();
2153+
uint64_t Size = DL.getTypeStoreSize(Val->getType());
2154+
if (Size == 0)
2155+
return;
2156+
2157+
// Conservatively set data at stored addresses and return with zero shadow to
2158+
// prevent shadow data races.
2159+
IRBuilder<> IRB(&I);
2160+
Value *Addr = I.getOperand(0);
2161+
const Align ShadowAlign = DFSF.getShadowAlign(InstAlignment);
2162+
DFSF.storeZeroPrimitiveShadow(Addr, Size, ShadowAlign, &I);
2163+
DFSF.setShadow(&I, DFSF.DFS.getZeroShadow(&I));
2164+
}
2165+
2166+
void DFSanVisitor::visitAtomicRMWInst(AtomicRMWInst &I) {
2167+
visitCASOrRMW(I.getAlign(), I);
2168+
// TODO: The ordering change follows MSan. It is possible not to change
2169+
// ordering because we always set and use 0 shadows.
2170+
I.setOrdering(addReleaseOrdering(I.getOrdering()));
2171+
}
2172+
2173+
void DFSanVisitor::visitAtomicCmpXchgInst(AtomicCmpXchgInst &I) {
2174+
visitCASOrRMW(I.getAlign(), I);
2175+
// TODO: The ordering change follows MSan. It is possible not to change
2176+
// ordering because we always set and use 0 shadows.
2177+
I.setSuccessOrdering(addReleaseOrdering(I.getSuccessOrdering()));
2178+
}
2179+
20722180
void DFSanVisitor::visitUnaryOperator(UnaryOperator &UO) {
20732181
visitInstOperands(UO);
20742182
}

0 commit comments

Comments
 (0)