Skip to content

Commit 9588b68

Browse files
committed
[asan] Implemented intrinsic for the custom calling convention similar used by HWASan for X86.
The implementation uses the int_asan_check_memaccess intrinsic to instrument the code. The intrinsic is replaced by a call to a function which performs the access check. The generated function names encode the input register name as a number using Reg - X86::NoRegister formula. Reviewed By: vitalybuka Differential Revision: https://reviews.llvm.org/D107850
1 parent ce1dc9d commit 9588b68

File tree

11 files changed

+815
-3
lines changed

11 files changed

+815
-3
lines changed

llvm/include/llvm/IR/Intrinsics.td

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1339,8 +1339,8 @@ def int_donothing : DefaultAttrsIntrinsic<[], [], [IntrNoMem, IntrWillReturn]>;
13391339
def int_sideeffect : DefaultAttrsIntrinsic<[], [], [IntrInaccessibleMemOnly, IntrWillReturn]>;
13401340

13411341
// The pseudoprobe intrinsic works as a place holder to the block it probes.
1342-
// Like the sideeffect intrinsic defined above, this intrinsic is treated by the
1343-
// optimizer as having opaque side effects so that it won't be get rid of or moved
1342+
// Like the sideeffect intrinsic defined above, this intrinsic is treated by the
1343+
// optimizer as having opaque side effects so that it won't be get rid of or moved
13441344
// out of the block it probes.
13451345
def int_pseudoprobe : Intrinsic<[], [llvm_i64_ty, llvm_i64_ty, llvm_i32_ty, llvm_i64_ty],
13461346
[IntrInaccessibleMemOnly, IntrWillReturn]>;
@@ -1637,6 +1637,9 @@ def int_icall_branch_funnel : DefaultAttrsIntrinsic<[], [llvm_vararg_ty], []>;
16371637
def int_load_relative: DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_ptr_ty, llvm_anyint_ty],
16381638
[IntrReadMem, IntrArgMemOnly]>;
16391639

1640+
def int_asan_check_memaccess :
1641+
Intrinsic<[],[llvm_ptr_ty, llvm_i32_ty], [ImmArg<ArgIndex<1>>]>;
1642+
16401643
def int_hwasan_check_memaccess :
16411644
Intrinsic<[], [llvm_ptr_ty, llvm_ptr_ty, llvm_i32_ty],
16421645
[ImmArg<ArgIndex<2>>]>;

llvm/include/llvm/Transforms/Instrumentation/AddressSanitizer.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,16 @@ ModulePass *createModuleAddressSanitizerLegacyPassPass(
155155
bool UseOdrIndicator = true,
156156
AsanDtorKind DestructorKind = AsanDtorKind::Global);
157157

158+
struct ASanAccessInfo {
159+
const int32_t Packed;
160+
const uint8_t AccessSizeIndex;
161+
const bool IsWrite;
162+
const bool CompileKernel;
163+
164+
explicit ASanAccessInfo(int32_t Packed);
165+
ASanAccessInfo(bool IsWrite, bool CompileKernel, uint8_t AccessSizeIndex);
166+
};
167+
158168
} // namespace llvm
159169

160170
#endif

llvm/lib/Target/X86/X86AsmPrinter.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,8 @@ static void emitNonLazyStubs(MachineModuleInfo *MMI, MCStreamer &OutStreamer) {
753753
void X86AsmPrinter::emitEndOfAsmFile(Module &M) {
754754
const Triple &TT = TM.getTargetTriple();
755755

756+
emitAsanMemaccessSymbols(M);
757+
756758
if (TT.isOSBinFormatMachO()) {
757759
// Mach-O uses non-lazy symbol stubs to encode per-TU information into
758760
// global table for symbol lookup.

llvm/lib/Target/X86/X86AsmPrinter.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class MCCodeEmitter;
2323
class MCStreamer;
2424
class X86Subtarget;
2525
class TargetMachine;
26+
struct ASanAccessInfo;
2627

2728
class LLVM_LIBRARY_VISIBILITY X86AsmPrinter : public AsmPrinter {
2829
const X86Subtarget *Subtarget = nullptr;
@@ -98,6 +99,23 @@ class LLVM_LIBRARY_VISIBILITY X86AsmPrinter : public AsmPrinter {
9899

99100
void LowerFENTRY_CALL(const MachineInstr &MI, X86MCInstLower &MCIL);
100101

102+
// Address sanitizer specific lowering for X86.
103+
void LowerASAN_CHECK_MEMACCESS(const MachineInstr &MI);
104+
void emitAsanMemaccessSymbols(Module &M);
105+
void emitAsanMemaccessPartial(Module &M, unsigned Reg,
106+
const ASanAccessInfo &AccessInfo,
107+
MCSubtargetInfo &STI);
108+
void emitAsanMemaccessFull(Module &M, unsigned Reg,
109+
const ASanAccessInfo &AccessInfo,
110+
MCSubtargetInfo &STI);
111+
void emitAsanReportError(Module &M, unsigned Reg,
112+
const ASanAccessInfo &AccessInfo,
113+
MCSubtargetInfo &STI);
114+
115+
typedef std::tuple<unsigned /*Reg*/, uint32_t /*AccessInfo*/>
116+
AsanMemaccessTuple;
117+
std::map<AsanMemaccessTuple, MCSymbol *> AsanMemaccessSymbols;
118+
101119
// Choose between emitting .seh_ directives and .cv_fpo_ directives.
102120
void EmitSEHInstruction(const MachineInstr *MI);
103121

llvm/lib/Target/X86/X86InstrCompiler.td

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,17 @@ let isPseudo = 1, SchedRW = [WriteSystem] in {
260260
"#SEH_Epilogue", []>;
261261
}
262262

263+
//===----------------------------------------------------------------------===//
264+
// Pseudo instructions used by address sanitizer.
265+
//===----------------------------------------------------------------------===//
266+
let
267+
Defs = [R8, EFLAGS] in {
268+
def ASAN_CHECK_MEMACCESS : PseudoI<
269+
(outs), (ins GR64NoR8:$addr, i32imm:$accessinfo),
270+
[(int_asan_check_memaccess GR64NoR8:$addr, (i32 timm:$accessinfo))]>,
271+
Sched<[]>;
272+
}
273+
263274
//===----------------------------------------------------------------------===//
264275
// Pseudo instructions used by segmented stacks.
265276
//
@@ -960,7 +971,7 @@ multiclass ATOMIC_RMW_BINOP<bits<8> opc8, bits<8> opc, string mnemonic,
960971
!strconcat(mnemonic, "{l}\t{$val, $ptr|$ptr, $val}"),
961972
[(set
962973
GR32:$dst,
963-
(!cast<PatFrag>(frag # "_32") addr:$ptr, GR32:$val))]>,
974+
(!cast<PatFrag>(frag # "_32") addr:$ptr, GR32:$val))]>,
964975
OpSize32;
965976
def NAME#64 : RI<opc, MRMSrcMem, (outs GR64:$dst),
966977
(ins GR64:$val, i64mem:$ptr),

llvm/lib/Target/X86/X86MCInstLower.cpp

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,11 @@
4343
#include "llvm/MC/MCStreamer.h"
4444
#include "llvm/MC/MCSymbol.h"
4545
#include "llvm/MC/MCSymbolELF.h"
46+
#include "llvm/Support/TargetRegistry.h"
4647
#include "llvm/Target/TargetLoweringObjectFile.h"
4748
#include "llvm/Target/TargetMachine.h"
49+
#include "llvm/Transforms/Instrumentation/AddressSanitizer.h"
50+
#include "llvm/Transforms/Instrumentation/AddressSanitizerCommon.h"
4851

4952
using namespace llvm;
5053

@@ -1323,6 +1326,242 @@ void X86AsmPrinter::LowerFENTRY_CALL(const MachineInstr &MI,
13231326
.addExpr(Op));
13241327
}
13251328

1329+
void X86AsmPrinter::LowerASAN_CHECK_MEMACCESS(const MachineInstr &MI) {
1330+
// FIXME: Make this work on non-ELF.
1331+
if (!TM.getTargetTriple().isOSBinFormatELF()) {
1332+
report_fatal_error("llvm.asan.check.memaccess only supported on ELF");
1333+
return;
1334+
}
1335+
1336+
unsigned Reg = MI.getOperand(0).getReg().id();
1337+
ASanAccessInfo AccessInfo(MI.getOperand(1).getImm());
1338+
1339+
MCSymbol *&Sym = AsanMemaccessSymbols[{Reg, AccessInfo.Packed}];
1340+
if (!Sym) {
1341+
std::string Name = AccessInfo.IsWrite ? "store" : "load";
1342+
std::string SymName = "__asan_check_" + Name +
1343+
utostr(1 << AccessInfo.AccessSizeIndex) + "_rn" +
1344+
utostr(Reg);
1345+
Sym = OutContext.getOrCreateSymbol(SymName);
1346+
}
1347+
1348+
EmitAndCountInstruction(
1349+
MCInstBuilder(X86::CALL64pcrel32)
1350+
.addExpr(MCSymbolRefExpr::create(Sym, OutContext)));
1351+
}
1352+
1353+
void X86AsmPrinter::emitAsanMemaccessPartial(Module &M, unsigned Reg,
1354+
const ASanAccessInfo &AccessInfo,
1355+
MCSubtargetInfo &STI) {
1356+
assert(AccessInfo.AccessSizeIndex == 0 || AccessInfo.AccessSizeIndex == 1 ||
1357+
AccessInfo.AccessSizeIndex == 2);
1358+
assert(Reg != X86::R8);
1359+
1360+
uint64_t ShadowBase;
1361+
int MappingScale;
1362+
bool OrShadowOffset;
1363+
getAddressSanitizerParams(
1364+
Triple(M.getTargetTriple()), M.getDataLayout().getPointerSizeInBits(),
1365+
AccessInfo.CompileKernel, &ShadowBase, &MappingScale, &OrShadowOffset);
1366+
1367+
OutStreamer->emitInstruction(
1368+
MCInstBuilder(X86::MOV64rr).addReg(X86::R8).addReg(X86::NoRegister + Reg),
1369+
STI);
1370+
OutStreamer->emitInstruction(MCInstBuilder(X86::SHR64ri)
1371+
.addReg(X86::R8)
1372+
.addReg(X86::NoRegister)
1373+
.addImm(MappingScale),
1374+
STI);
1375+
if (OrShadowOffset) {
1376+
OutStreamer->emitInstruction(MCInstBuilder(X86::OR64ri32)
1377+
.addReg(X86::NoRegister)
1378+
.addReg(X86::R8)
1379+
.addImm(ShadowBase),
1380+
STI);
1381+
OutStreamer->emitInstruction(MCInstBuilder(X86::MOV8rm)
1382+
.addReg(X86::R8B)
1383+
.addReg(X86::R8)
1384+
.addImm(1)
1385+
.addReg(X86::NoRegister)
1386+
.addImm(0)
1387+
.addReg(X86::NoRegister),
1388+
STI);
1389+
OutStreamer->emitInstruction(
1390+
MCInstBuilder(X86::TEST8rr).addReg(X86::R8B).addReg(X86::R8B), STI);
1391+
} else {
1392+
OutStreamer->emitInstruction(MCInstBuilder(X86::MOVSX32rm8)
1393+
.addReg(X86::R8D)
1394+
.addReg(X86::R8)
1395+
.addImm(1)
1396+
.addReg(X86::NoRegister)
1397+
.addImm(ShadowBase)
1398+
.addReg(X86::NoRegister),
1399+
STI);
1400+
OutStreamer->emitInstruction(
1401+
MCInstBuilder(X86::TEST32rr).addReg(X86::R8D).addReg(X86::R8D), STI);
1402+
}
1403+
MCSymbol *AdditionalCheck = OutContext.createTempSymbol();
1404+
OutStreamer->emitInstruction(
1405+
MCInstBuilder(X86::JCC_1)
1406+
.addExpr(MCSymbolRefExpr::create(AdditionalCheck, OutContext))
1407+
.addImm(X86::COND_NE),
1408+
STI);
1409+
MCSymbol *ReturnSym = OutContext.createTempSymbol();
1410+
OutStreamer->emitLabel(ReturnSym);
1411+
OutStreamer->emitInstruction(MCInstBuilder(getRetOpcode(*Subtarget)), STI);
1412+
1413+
// Shadow byte is non-zero so we need to perform additional checks.
1414+
OutStreamer->emitLabel(AdditionalCheck);
1415+
OutStreamer->emitInstruction(MCInstBuilder(X86::PUSH64r).addReg(X86::RCX),
1416+
STI);
1417+
OutStreamer->emitInstruction(MCInstBuilder(X86::MOV64rr)
1418+
.addReg(X86::RCX)
1419+
.addReg(X86::NoRegister + Reg),
1420+
STI);
1421+
const size_t Granularity = 1ULL << MappingScale;
1422+
OutStreamer->emitInstruction(MCInstBuilder(X86::AND32ri8)
1423+
.addReg(X86::NoRegister)
1424+
.addReg(X86::ECX)
1425+
.addImm(Granularity - 1),
1426+
STI);
1427+
if (AccessInfo.AccessSizeIndex == 1) {
1428+
OutStreamer->emitInstruction(MCInstBuilder(X86::ADD32ri8)
1429+
.addReg(X86::NoRegister)
1430+
.addReg(X86::ECX)
1431+
.addImm(1),
1432+
STI);
1433+
} else if (AccessInfo.AccessSizeIndex == 2) {
1434+
OutStreamer->emitInstruction(MCInstBuilder(X86::ADD32ri8)
1435+
.addReg(X86::NoRegister)
1436+
.addReg(X86::ECX)
1437+
.addImm(3),
1438+
STI);
1439+
}
1440+
1441+
OutStreamer->emitInstruction(
1442+
MCInstBuilder(X86::CMP32rr).addReg(X86::ECX).addReg(X86::R8D).addImm(1),
1443+
STI);
1444+
OutStreamer->emitInstruction(MCInstBuilder(X86::POP64r).addReg(X86::RCX),
1445+
STI);
1446+
OutStreamer->emitInstruction(
1447+
MCInstBuilder(X86::JCC_1)
1448+
.addExpr(MCSymbolRefExpr::create(ReturnSym, OutContext))
1449+
.addImm(X86::COND_L),
1450+
STI);
1451+
1452+
emitAsanReportError(M, Reg, AccessInfo, STI);
1453+
}
1454+
1455+
void X86AsmPrinter::emitAsanMemaccessFull(Module &M, unsigned Reg,
1456+
const ASanAccessInfo &AccessInfo,
1457+
MCSubtargetInfo &STI) {
1458+
assert(AccessInfo.AccessSizeIndex == 3 || AccessInfo.AccessSizeIndex == 4);
1459+
assert(Reg != X86::R8);
1460+
1461+
uint64_t ShadowBase;
1462+
int MappingScale;
1463+
bool OrShadowOffset;
1464+
getAddressSanitizerParams(
1465+
Triple(M.getTargetTriple()), M.getDataLayout().getPointerSizeInBits(),
1466+
AccessInfo.CompileKernel, &ShadowBase, &MappingScale, &OrShadowOffset);
1467+
1468+
OutStreamer->emitInstruction(
1469+
MCInstBuilder(X86::MOV64rr).addReg(X86::R8).addReg(X86::NoRegister + Reg),
1470+
STI);
1471+
OutStreamer->emitInstruction(MCInstBuilder(X86::SHR64ri)
1472+
.addReg(X86::R8)
1473+
.addReg(X86::R8)
1474+
.addImm(MappingScale),
1475+
STI);
1476+
if (OrShadowOffset) {
1477+
OutStreamer->emitInstruction(MCInstBuilder(X86::OR64ri32)
1478+
.addReg(X86::R8)
1479+
.addReg(X86::R8)
1480+
.addImm(ShadowBase),
1481+
STI);
1482+
auto OpCode = AccessInfo.AccessSizeIndex == 3 ? X86::CMP8mi : X86::CMP16mi8;
1483+
OutStreamer->emitInstruction(MCInstBuilder(OpCode)
1484+
.addReg(X86::R8)
1485+
.addImm(1)
1486+
.addReg(X86::NoRegister)
1487+
.addImm(0)
1488+
.addReg(X86::NoRegister)
1489+
.addImm(0),
1490+
STI);
1491+
} else {
1492+
auto OpCode = AccessInfo.AccessSizeIndex == 3 ? X86::CMP8mi : X86::CMP16mi8;
1493+
OutStreamer->emitInstruction(MCInstBuilder(OpCode)
1494+
.addReg(X86::R8)
1495+
.addImm(1)
1496+
.addReg(X86::NoRegister)
1497+
.addImm(ShadowBase)
1498+
.addReg(X86::NoRegister)
1499+
.addImm(0),
1500+
STI);
1501+
}
1502+
MCSymbol *ReportCode = OutContext.createTempSymbol();
1503+
OutStreamer->emitInstruction(
1504+
MCInstBuilder(X86::JCC_1)
1505+
.addExpr(MCSymbolRefExpr::create(ReportCode, OutContext))
1506+
.addImm(X86::COND_NE),
1507+
STI);
1508+
MCSymbol *ReturnSym = OutContext.createTempSymbol();
1509+
OutStreamer->emitLabel(ReturnSym);
1510+
OutStreamer->emitInstruction(MCInstBuilder(getRetOpcode(*Subtarget)), STI);
1511+
1512+
OutStreamer->emitLabel(ReportCode);
1513+
emitAsanReportError(M, Reg, AccessInfo, STI);
1514+
}
1515+
1516+
void X86AsmPrinter::emitAsanReportError(Module &M, unsigned Reg,
1517+
const ASanAccessInfo &AccessInfo,
1518+
MCSubtargetInfo &STI) {
1519+
std::string Name = AccessInfo.IsWrite ? "store" : "load";
1520+
MCSymbol *ReportError = OutContext.getOrCreateSymbol(
1521+
"__asan_report_" + Name + utostr(1 << AccessInfo.AccessSizeIndex));
1522+
OutStreamer->emitInstruction(MCInstBuilder(X86::MOV64rr)
1523+
.addReg(X86::RDI)
1524+
.addReg(X86::NoRegister + Reg),
1525+
STI);
1526+
OutStreamer->emitInstruction(
1527+
MCInstBuilder(X86::JMP_1)
1528+
.addExpr(MCSymbolRefExpr::create(ReportError, OutContext)),
1529+
STI);
1530+
}
1531+
1532+
void X86AsmPrinter::emitAsanMemaccessSymbols(Module &M) {
1533+
if (AsanMemaccessSymbols.empty())
1534+
return;
1535+
1536+
const Triple &TT = TM.getTargetTriple();
1537+
assert(TT.isOSBinFormatELF());
1538+
std::unique_ptr<MCSubtargetInfo> STI(
1539+
TM.getTarget().createMCSubtargetInfo(TT.str(), "", ""));
1540+
assert(STI && "Unable to create subtarget info");
1541+
1542+
for (auto &P : AsanMemaccessSymbols) {
1543+
MCSymbol *Sym = P.second;
1544+
OutStreamer->SwitchSection(OutContext.getELFSection(
1545+
".text.hot", ELF::SHT_PROGBITS,
1546+
ELF::SHF_EXECINSTR | ELF::SHF_ALLOC | ELF::SHF_GROUP, 0, Sym->getName(),
1547+
/*IsComdat=*/true));
1548+
1549+
OutStreamer->emitSymbolAttribute(Sym, MCSA_ELF_TypeFunction);
1550+
OutStreamer->emitSymbolAttribute(Sym, MCSA_Weak);
1551+
OutStreamer->emitSymbolAttribute(Sym, MCSA_Hidden);
1552+
OutStreamer->emitLabel(Sym);
1553+
1554+
unsigned Reg = std::get<0>(P.first);
1555+
ASanAccessInfo AccessInfo(std::get<1>(P.first));
1556+
1557+
if (AccessInfo.AccessSizeIndex < 3) {
1558+
emitAsanMemaccessPartial(M, Reg, AccessInfo, *STI);
1559+
} else {
1560+
emitAsanMemaccessFull(M, Reg, AccessInfo, *STI);
1561+
}
1562+
}
1563+
}
1564+
13261565
void X86AsmPrinter::LowerPATCHABLE_OP(const MachineInstr &MI,
13271566
X86MCInstLower &MCIL) {
13281567
// PATCHABLE_OP minsize, opcode, operands
@@ -2563,6 +2802,9 @@ void X86AsmPrinter::emitInstruction(const MachineInstr *MI) {
25632802
EmitAndCountInstruction(MCInstBuilder(getRetOpcode(*Subtarget)));
25642803
return;
25652804

2805+
case X86::ASAN_CHECK_MEMACCESS:
2806+
return LowerASAN_CHECK_MEMACCESS(*MI);
2807+
25662808
case X86::MORESTACK_RET_RESTORE_R10:
25672809
// Return, then restore R10.
25682810
EmitAndCountInstruction(MCInstBuilder(getRetOpcode(*Subtarget)));

llvm/lib/Target/X86/X86RegisterInfo.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,12 @@ def GR64 : RegisterClass<"X86", [i64], 64,
436436
(add RAX, RCX, RDX, RSI, RDI, R8, R9, R10, R11,
437437
RBX, R14, R15, R12, R13, RBP, RSP, RIP)>;
438438

439+
// GR64 - 64-bit GPRs without R8 and RIP. Could be used when emitting code for
440+
// intrinsics, which use implict input registers.
441+
def GR64NoR8 : RegisterClass<"X86", [i64], 64,
442+
(add RAX, RCX, RDX, RSI, RDI, R9, R10, R11,
443+
RBX, R14, R15, R12, R13, RBP, RSP)>;
444+
439445
// Segment registers for use by MOV instructions (and others) that have a
440446
// segment register as one operand. Always contain a 16-bit segment
441447
// descriptor.

0 commit comments

Comments
 (0)