llvm.org GIT mirror llvm / f159a11
hwasan: Add a tag_offset DWARF attribute to instrumented stack variables. The goal is to improve hwasan's error reporting for stack use-after-return by recording enough information to allow the specific variable that was accessed to be identified based on the pointer's tag. Currently we record the PC and lower bits of SP for each stack frame we create (which will eventually be enough to derive the base tag used by the stack frame) but that's not enough to determine the specific tag for each variable, which is the stack frame's base tag XOR a value (the "tag offset") that is unique for each variable in a function. In IR, the tag offset is most naturally represented as part of a location expression on the llvm.dbg.declare instruction. However, the presence of the tag offset in the variable's actual location expression is likely to confuse debuggers which won't know about tag offsets, and moreover the tag offset is not required for a debugger to determine the location of the variable on the stack, so at the DWARF level it is represented as an attribute so that it will be ignored by debuggers that don't know about it. Differential Revision: https://reviews.llvm.org/D63119 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@363635 91177308-0d34-0410-b5e6-96231b3b80d8 Peter Collingbourne 28 days ago
12 changed file(s) with 144 addition(s) and 8 deletion(s). Raw diff Collapse all Expand all
47034703 (``16`` and ``DW_ATE_signed`` here, respectively) to which the top of the
47044704 expression stack is to be converted. Maps into a ``DW_OP_convert`` operation
47054705 that references a base type constructed from the supplied values.
4706 - ``DW_OP_LLVM_tag_offset, tag_offset`` specifies that a memory tag should be
4707 optionally applied to the pointer. The memory tag is derived from the
4708 given tag offset in an implementation-defined manner.
47064709 - ``DW_OP_swap`` swaps top two stack entries.
47074710 - ``DW_OP_xderef`` provides extended dereference mechanism. The entry at the top
47084711 of the stack is treated as an address. The second stack entry is treated as an
385385 HANDLE_DW_AT(0x3e00, LLVM_include_path, 0, LLVM)
386386 HANDLE_DW_AT(0x3e01, LLVM_config_macros, 0, LLVM)
387387 HANDLE_DW_AT(0x3e02, LLVM_isysroot, 0, LLVM)
388 HANDLE_DW_AT(0x3e03, LLVM_tag_offset, 0, LLVM)
388389 // Apple extensions.
389390 HANDLE_DW_AT(0x3fe1, APPLE_optimized, 0, APPLE)
390391 HANDLE_DW_AT(0x3fe2, APPLE_flags, 0, APPLE)
128128 #include "llvm/BinaryFormat/Dwarf.def"
129129 DW_OP_lo_user = 0xe0,
130130 DW_OP_hi_user = 0xff,
131 DW_OP_LLVM_fragment = 0x1000, ///< Only used in LLVM metadata.
132 DW_OP_LLVM_convert = 0x1001 ///< Only used in LLVM metadata.
131 DW_OP_LLVM_fragment = 0x1000, ///< Only used in LLVM metadata.
132 DW_OP_LLVM_convert = 0x1001, ///< Only used in LLVM metadata.
133 DW_OP_LLVM_tag_offset = 0x1002, ///< Only used in LLVM metadata.
133134 };
134135
135136 enum TypeKind : uint8_t {
146146 return "DW_OP_LLVM_convert";
147147 case DW_OP_LLVM_fragment:
148148 return "DW_OP_LLVM_fragment";
149 case DW_OP_LLVM_tag_offset:
150 return "DW_OP_LLVM_tag_offset";
149151 }
150152 }
151153
156158 #include "llvm/BinaryFormat/Dwarf.def"
157159 .Case("DW_OP_LLVM_convert", DW_OP_LLVM_convert)
158160 .Case("DW_OP_LLVM_fragment", DW_OP_LLVM_fragment)
161 .Case("DW_OP_LLVM_tag_offset", DW_OP_LLVM_tag_offset)
159162 .Default(0);
160163 }
161164
682682 NVPTXAddressSpace ? *NVPTXAddressSpace : NVPTX_ADDR_local_space);
683683 }
684684 addBlock(*VariableDie, dwarf::DW_AT_location, DwarfExpr.finalize());
685 if (DwarfExpr.TagOffset)
686 addUInt(*VariableDie, dwarf::DW_AT_LLVM_tag_offset, dwarf::DW_FORM_data1,
687 *DwarfExpr.TagOffset);
685688
686689 return VariableDie;
687690 }
437437 emitOp(dwarf::DW_OP_deref_size);
438438 emitData1(Op->getArg(0));
439439 break;
440 case dwarf::DW_OP_LLVM_tag_offset:
441 TagOffset = Op->getArg(0);
442 break;
440443 default:
441444 llvm_unreachable("unhandled opcode found in expression");
442445 }
139139 return LocationKind == Implicit;
140140 }
141141
142 Optional TagOffset;
143
142144 protected:
143145 /// Push a DW_OP_piece / DW_OP_bit_piece for emitting later, if one is needed
144146 /// to represent a subregister.
834834 case dwarf::DW_OP_constu:
835835 case dwarf::DW_OP_deref_size:
836836 case dwarf::DW_OP_plus_uconst:
837 case dwarf::DW_OP_LLVM_tag_offset:
837838 return 2;
838839 default:
839840 return 1;
875876 break;
876877 }
877878 case dwarf::DW_OP_LLVM_convert:
879 case dwarf::DW_OP_LLVM_tag_offset:
878880 case dwarf::DW_OP_constu:
879881 case dwarf::DW_OP_plus_uconst:
880882 case dwarf::DW_OP_plus:
904906 unsigned N = getNumElements();
905907 if (isValid() && N > 0) {
906908 switch (getElement(N-1)) {
907 case dwarf::DW_OP_stack_value: return true;
909 case dwarf::DW_OP_stack_value:
910 case dwarf::DW_OP_LLVM_tag_offset:
911 return true;
908912 case dwarf::DW_OP_LLVM_fragment:
909913 return N > 1 && getElement(N-2) == dwarf::DW_OP_stack_value;
910914 default: break;
2020 #include "llvm/IR/Constant.h"
2121 #include "llvm/IR/Constants.h"
2222 #include "llvm/IR/DataLayout.h"
23 #include "llvm/IR/DebugInfoMetadata.h"
2324 #include "llvm/IR/DerivedTypes.h"
2425 #include "llvm/IR/Function.h"
2526 #include "llvm/IR/IRBuilder.h"
204205 bool tagAlloca(IRBuilder<> &IRB, AllocaInst *AI, Value *Tag);
205206 Value *tagPointer(IRBuilder<> &IRB, Type *Ty, Value *PtrLong, Value *Tag);
206207 Value *untagPointer(IRBuilder<> &IRB, Value *PtrLong);
207 bool instrumentStack(SmallVectorImpl &Allocas,
208 SmallVectorImpl &RetVec, Value *StackTag);
208 bool instrumentStack(
209 SmallVectorImpl &Allocas,
210 DenseMap> &AllocaDeclareMap,
211 SmallVectorImpl &RetVec, Value *StackTag);
209212 bool instrumentLandingPads(SmallVectorImpl &RetVec);
210213 Value *getNextTagWithCall(IRBuilder<> &IRB);
211214 Value *getStackBaseTag(IRBuilder<> &IRB);
983986
984987 bool HWAddressSanitizer::instrumentStack(
985988 SmallVectorImpl &Allocas,
989 DenseMap> &AllocaDeclareMap,
986990 SmallVectorImpl &RetVec, Value *StackTag) {
987991 // Ideally, we want to calculate tagged stack base pointer, and rewrite all
988992 // alloca addresses using that. Unfortunately, offsets are not known yet
10051009 Use &U = *UI++;
10061010 if (U.getUser() != AILong)
10071011 U.set(Replacement);
1012 }
1013
1014 for (auto *DDI : AllocaDeclareMap.lookup(AI)) {
1015 DIExpression *OldExpr = DDI->getExpression();
1016 DIExpression *NewExpr = DIExpression::append(
1017 OldExpr, {dwarf::DW_OP_LLVM_tag_offset, RetagMask(N)});
1018 DDI->setArgOperand(2, MetadataAsValue::get(*C, NewExpr));
10081019 }
10091020
10101021 tagAlloca(IRB, AI, Tag);
10501061 SmallVector AllocasToInstrument;
10511062 SmallVector RetVec;
10521063 SmallVector LandingPadVec;
1064 DenseMap> AllocaDeclareMap;
10531065 for (auto &BB : F) {
10541066 for (auto &Inst : BB) {
10551067 if (ClInstrumentStack)
10681080 isa(Inst))
10691081 RetVec.push_back(&Inst);
10701082
1083 if (auto *DDI = dyn_cast(&Inst))
1084 if (auto *Alloca = dyn_cast_or_null(DDI->getAddress()))
1085 AllocaDeclareMap[Alloca].push_back(DDI);
1086
10711087 if (ClInstrumentLandingPads && isa(Inst))
10721088 LandingPadVec.push_back(&Inst);
10731089
11061122 if (!AllocasToInstrument.empty()) {
11071123 Value *StackTag =
11081124 ClGenerateTagsWithCalls ? nullptr : getStackBaseTag(EntryIRB);
1109 Changed |= instrumentStack(AllocasToInstrument, RetVec, StackTag);
1125 Changed |= instrumentStack(AllocasToInstrument, AllocaDeclareMap, RetVec,
1126 StackTag);
11101127 }
11111128
11121129 // If we split the entry block, move any allocas that were originally in the
88 ; CHECK-SAME: !DIExpression(DW_OP_deref, DW_OP_plus_uconst, 3, DW_OP_LLVM_fragment, 3, 7),
99 ; CHECK-SAME: !DIExpression(DW_OP_constu, 2, DW_OP_swap, DW_OP_xderef),
1010 ; CHECK-SAME: !DIExpression(DW_OP_plus_uconst, 3)
11 ; CHECK-SAME: !DIExpression(DW_OP_LLVM_convert, 16, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_signed)}
11 ; CHECK-SAME: !DIExpression(DW_OP_LLVM_convert, 16, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_signed)
12 ; CHECK-SAME: !DIExpression(DW_OP_LLVM_tag_offset, 1)}
1213
13 !named = !{!0, !1, !2, !3, !4, !5, !6, !7}
14 !named = !{!0, !1, !2, !3, !4, !5, !6, !7, !8}
1415
1516 !0 = !DIExpression()
1617 !1 = !DIExpression(DW_OP_deref)
2021 !5 = !DIExpression(DW_OP_constu, 2, DW_OP_swap, DW_OP_xderef)
2122 !6 = !DIExpression(DW_OP_plus_uconst, 3)
2223 !7 = !DIExpression(DW_OP_LLVM_convert, 16, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_signed)
24 !8 = !DIExpression(DW_OP_LLVM_tag_offset, 1)
0 ; RUN: llc -o - %s | FileCheck %s
1
2 target triple="aarch64--"
3
4 ; CHECK: .Linfo_string4:
5 ; CHECK-NEXT: .asciz "a"
6 ; CHECK: .Linfo_string6:
7 ; CHECK-NEXT: .asciz "b"
8
9 ; CHECK: .byte 1 // DW_AT_LLVM_tag_offset
10 ; CHECK: .word .Linfo_string4 // DW_AT_name
11
12 ; CHECK: .byte 2 // DW_AT_LLVM_tag_offset
13 ; CHECK: .word .Linfo_string6 // DW_AT_name
14
15 define void @f() !dbg !6 {
16 entry:
17 %a = alloca i8*
18 %b = alloca i8*
19 call void @llvm.dbg.declare(metadata i8** %a, metadata !12, metadata !DIExpression(DW_OP_LLVM_tag_offset, 1)), !dbg !14
20 call void @llvm.dbg.declare(metadata i8** %b, metadata !13, metadata !DIExpression(DW_OP_LLVM_tag_offset, 2)), !dbg !14
21 ret void, !dbg !15
22 }
23
24 declare void @llvm.dbg.declare(metadata, metadata, metadata)
25
26 !llvm.dbg.cu = !{!0}
27 !llvm.module.flags = !{!3, !4}
28 !llvm.ident = !{!5}
29
30 !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
31 !1 = !DIFile(filename: "x.c", directory: "/")
32 !2 = !{}
33 !3 = !{i32 2, !"Dwarf Version", i32 4}
34 !4 = !{i32 2, !"Debug Info Version", i32 3}
35 !5 = !{!"clang"}
36 !6 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !7, isLocal: false, isDefinition: true, scopeLine: 1, flags:
37 DIFlagPrototyped, isOptimized: false, unit: !0, retainedNodes: !2)
38 !7 = !DISubroutineType(types: !8)
39 !8 = !{null, !9}
40 !9 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64)
41 !10 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !11)
42 !11 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
43 !12 = !DILocalVariable(name: "a", scope: !6, file: !1, line: 1, type: !9)
44 !13 = !DILocalVariable(name: "b", scope: !6, file: !1, line: 1, type: !9)
45 !14 = !DILocation(line: 1, column: 29, scope: !6)
46 !15 = !DILocation(line: 1, column: 37, scope: !6)
0 ; RUN: opt -hwasan -S -o - %s | FileCheck %s
1
2 target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
3 target triple = "aarch64--linux-android"
4
5 declare void @g(i8**, i8**, i8**, i8**, i8**, i8**)
6
7 define void @f() sanitize_hwaddress !dbg !6 {
8 entry:
9 %nodebug0 = alloca i8*
10 %nodebug1 = alloca i8*
11 %nodebug2 = alloca i8*
12 %nodebug3 = alloca i8*
13 %a = alloca i8*
14 %b = alloca i8*
15 ; CHECK: @llvm.dbg.declare{{.*}} !DIExpression(DW_OP_LLVM_tag_offset, 4)
16 call void @llvm.dbg.declare(metadata i8** %a, metadata !12, metadata !DIExpression()), !dbg !14
17 ; CHECK: @llvm.dbg.declare{{.*}} !DIExpression(DW_OP_LLVM_tag_offset, 4)
18 call void @llvm.dbg.declare(metadata i8** %a, metadata !12, metadata !DIExpression()), !dbg !14
19 ; CHECK: @llvm.dbg.declare{{.*}} !DIExpression(DW_OP_LLVM_tag_offset, 6)
20 call void @llvm.dbg.declare(metadata i8** %b, metadata !13, metadata !DIExpression()), !dbg !14
21 ; CHECK: @llvm.dbg.declare{{.*}} !DIExpression(DW_OP_LLVM_tag_offset, 6)
22 call void @llvm.dbg.declare(metadata i8** %b, metadata !13, metadata !DIExpression()), !dbg !14
23 call void @g(i8** %nodebug0, i8** %nodebug1, i8** %nodebug2, i8** %nodebug3, i8** %a, i8** %b)
24 ret void, !dbg !15
25 }
26
27 declare void @llvm.dbg.declare(metadata, metadata, metadata)
28
29 !llvm.dbg.cu = !{!0}
30 !llvm.module.flags = !{!3, !4}
31 !llvm.ident = !{!5}
32
33 !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
34 !1 = !DIFile(filename: "x.c", directory: "/")
35 !2 = !{}
36 !3 = !{i32 2, !"Dwarf Version", i32 4}
37 !4 = !{i32 2, !"Debug Info Version", i32 3}
38 !5 = !{!"clang"}
39 !6 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !7, isLocal: false, isDefinition: true, scopeLine: 1, flags:
40 DIFlagPrototyped, isOptimized: false, unit: !0, retainedNodes: !2)
41 !7 = !DISubroutineType(types: !8)
42 !8 = !{null, !9}
43 !9 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64)
44 !10 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !11)
45 !11 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
46 !12 = !DILocalVariable(name: "a", scope: !6, file: !1, line: 1, type: !9)
47 !13 = !DILocalVariable(name: "b", scope: !6, file: !1, line: 1, type: !9)
48 !14 = !DILocation(line: 1, column: 29, scope: !6)
49 !15 = !DILocation(line: 1, column: 37, scope: !6)