Skip to content

Commit c71a5bf

Browse files
authored
[msan] Unpoison indirect outputs for userspace when -msan-handle-asm-conservative is specified (#77393)
KMSAN defaults to `msan-handle-asm-conservative`, which inserts `__msan_instrument_asm_store` calls to unpoison indirect outputs in inline assembly (e.g. `=m` constraints in source). ```c unsigned f() { unsigned v; // __msan_instrument_asm_store unpoisons v before invoking the asm. asm("movl $1,%0" : "=m"(v)); return v; } ``` Extend the mechanism to userspace, but require explicit `-mllvm -msan-handle-asm-conservative` for experiments for now. As https://docs.kernel.org/dev-tools/kmsan.html#inline-assembly-instrumentation says, this approach may mask certain errors (an indirect output may not actually be initialized), but it also helps to avoid a lot of false positives. Link: google/sanitizers#192
1 parent 123ab34 commit c71a5bf

File tree

2 files changed

+73
-49
lines changed

2 files changed

+73
-49
lines changed

llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4103,7 +4103,11 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
41034103
// do the usual thing: check argument shadow and mark all outputs as
41044104
// clean. Note that any side effects of the inline asm that are not
41054105
// immediately visible in its constraints are not handled.
4106-
if (ClHandleAsmConservative && MS.CompileKernel)
4106+
// For now, handle inline asm by default for KMSAN.
4107+
bool HandleAsm = ClHandleAsmConservative.getNumOccurrences()
4108+
? ClHandleAsmConservative
4109+
: MS.CompileKernel;
4110+
if (HandleAsm)
41074111
visitAsmInstruction(CB);
41084112
else
41094113
visitInstruction(CB);
@@ -4557,7 +4561,15 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
45574561
return;
45584562
Value *SizeVal =
45594563
IRB.CreateTypeSize(MS.IntptrTy, DL.getTypeStoreSize(ElemTy));
4560-
IRB.CreateCall(MS.MsanInstrumentAsmStoreFn, {Operand, SizeVal});
4564+
if (MS.CompileKernel) {
4565+
IRB.CreateCall(MS.MsanInstrumentAsmStoreFn, {Operand, SizeVal});
4566+
} else {
4567+
// ElemTy, derived from elementtype(), does not encode the alignment of
4568+
// the pointer. Conservatively assume that the shadow memory is unaligned.
4569+
auto [ShadowPtr, _] =
4570+
getShadowOriginPtrUserspace(Operand, IRB, IRB.getInt8Ty(), Align(1));
4571+
IRB.CreateAlignedStore(getCleanShadow(ElemTy), ShadowPtr, Align(1));
4572+
}
45614573
}
45624574

45634575
/// Get the number of output arguments returned by pointers.

llvm/test/Instrumentation/MemorySanitizer/msan_asm_conservative.ll

Lines changed: 59 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
; Test for handling of asm constraints in MSan instrumentation.
2+
; RUN: opt < %s -msan-check-access-address=0 -msan-handle-asm-conservative=0 -S -passes=msan 2>&1 | \
3+
; RUN: FileCheck %s
4+
; RUN: opt < %s -msan-check-access-address=0 -msan-handle-asm-conservative=1 -S -passes=msan 2>&1 | \
5+
; RUN: FileCheck --check-prefixes=CHECK,USER-CONS %s
26
; RUN: opt < %s -msan-kernel=1 -msan-check-access-address=0 \
3-
; RUN: -msan-handle-asm-conservative=0 -S -passes=msan 2>&1 | FileCheck \
4-
; RUN: "-check-prefix=CHECK" %s
7+
; RUN: -msan-handle-asm-conservative=0 -S -passes=msan 2>&1 | FileCheck \
8+
; RUN: --check-prefixes=CHECK,KMSAN %s
59
; RUN: opt < %s -msan-kernel=1 -msan-check-access-address=0 \
6-
; RUN: -msan-handle-asm-conservative=1 -S -passes=msan 2>&1 | FileCheck \
7-
; RUN: "-check-prefixes=CHECK,CHECK-CONS" %s
10+
; RUN: -msan-handle-asm-conservative=1 -S -passes=msan 2>&1 | FileCheck \
11+
; RUN: --check-prefixes=CHECK,KMSAN,CHECK-CONS %s
812

913
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
1014
target triple = "x86_64-unknown-linux-gnu"
@@ -46,9 +50,9 @@ entry:
4650
; CHECK: [[IS1_F1:%.*]] = load i32, ptr @is1, align 4
4751
; CHECK: call void @__msan_warning
4852
; CHECK: call i32 asm "",{{.*}}(i32 [[IS1_F1]])
49-
; CHECK: [[PACK1_F1:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
50-
; CHECK: [[EXT1_F1:%.*]] = extractvalue { ptr, ptr } [[PACK1_F1]], 0
51-
; CHECK: store i32 0, ptr [[EXT1_F1]]
53+
; KMSAN: [[PACK1_F1:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
54+
; KMSAN: [[EXT1_F1:%.*]] = extractvalue { ptr, ptr } [[PACK1_F1]], 0
55+
; KMSAN: store i32 0, ptr [[EXT1_F1]]
5256

5357

5458
; Two input registers, two output registers:
@@ -69,14 +73,14 @@ entry:
6973
; CHECK: [[IS1_F2:%.*]] = load i32, ptr @is1, align 4
7074
; CHECK: [[IS2_F2:%.*]] = load i32, ptr @is2, align 4
7175
; CHECK: call void @__msan_warning
72-
; CHECK: call void @__msan_warning
76+
; KMSAN: call void @__msan_warning
7377
; CHECK: call { i32, i32 } asm "",{{.*}}(i32 [[IS1_F2]], i32 [[IS2_F2]])
74-
; CHECK: [[PACK1_F2:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
75-
; CHECK: [[EXT1_F2:%.*]] = extractvalue { ptr, ptr } [[PACK1_F2]], 0
76-
; CHECK: store i32 0, ptr [[EXT1_F2]]
77-
; CHECK: [[PACK2_F2:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
78-
; CHECK: [[EXT2_F2:%.*]] = extractvalue { ptr, ptr } [[PACK2_F2]], 0
79-
; CHECK: store i32 0, ptr [[EXT2_F2]]
78+
; KMSAN: [[PACK1_F2:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
79+
; KMSAN: [[EXT1_F2:%.*]] = extractvalue { ptr, ptr } [[PACK1_F2]], 0
80+
; KMSAN: store i32 0, ptr [[EXT1_F2]]
81+
; KMSAN: [[PACK2_F2:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
82+
; KMSAN: [[EXT2_F2:%.*]] = extractvalue { ptr, ptr } [[PACK2_F2]], 0
83+
; KMSAN: store i32 0, ptr [[EXT2_F2]]
8084

8185
; Input same as output, used twice:
8286
; asm("" : "=r" (id1), "=r" (id2) : "r" (id1), "r" (id2));
@@ -96,14 +100,14 @@ entry:
96100
; CHECK: [[ID1_F3:%.*]] = load i32, ptr @id1, align 4
97101
; CHECK: [[ID2_F3:%.*]] = load i32, ptr @id2, align 4
98102
; CHECK: call void @__msan_warning
99-
; CHECK: call void @__msan_warning
103+
; KMSAN: call void @__msan_warning
100104
; CHECK: call { i32, i32 } asm "",{{.*}}(i32 [[ID1_F3]], i32 [[ID2_F3]])
101-
; CHECK: [[PACK1_F3:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
102-
; CHECK: [[EXT1_F3:%.*]] = extractvalue { ptr, ptr } [[PACK1_F3]], 0
103-
; CHECK: store i32 0, ptr [[EXT1_F3]]
104-
; CHECK: [[PACK2_F3:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
105-
; CHECK: [[EXT2_F3:%.*]] = extractvalue { ptr, ptr } [[PACK2_F3]], 0
106-
; CHECK: store i32 0, ptr [[EXT2_F3]]
105+
; KMSAN: [[PACK1_F3:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
106+
; KMSAN: [[EXT1_F3:%.*]] = extractvalue { ptr, ptr } [[PACK1_F3]], 0
107+
; KMSAN: store i32 0, ptr [[EXT1_F3]]
108+
; KMSAN: [[PACK2_F3:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
109+
; KMSAN: [[EXT2_F3:%.*]] = extractvalue { ptr, ptr } [[PACK2_F3]], 0
110+
; KMSAN: store i32 0, ptr [[EXT2_F3]]
107111

108112

109113
; One of the input registers is also an output:
@@ -124,14 +128,14 @@ entry:
124128
; CHECK: [[ID1_F4:%.*]] = load i32, ptr @id1, align 4
125129
; CHECK: [[IS1_F4:%.*]] = load i32, ptr @is1, align 4
126130
; CHECK: call void @__msan_warning
127-
; CHECK: call void @__msan_warning
131+
; KMSAN: call void @__msan_warning
128132
; CHECK: call { i32, i32 } asm "",{{.*}}(i32 [[ID1_F4]], i32 [[IS1_F4]])
129-
; CHECK: [[PACK1_F4:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
130-
; CHECK: [[EXT1_F4:%.*]] = extractvalue { ptr, ptr } [[PACK1_F4]], 0
131-
; CHECK: store i32 0, ptr [[EXT1_F4]]
132-
; CHECK: [[PACK2_F4:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
133-
; CHECK: [[EXT2_F4:%.*]] = extractvalue { ptr, ptr } [[PACK2_F4]], 0
134-
; CHECK: store i32 0, ptr [[EXT2_F4]]
133+
; KMSAN: [[PACK1_F4:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
134+
; KMSAN: [[EXT1_F4:%.*]] = extractvalue { ptr, ptr } [[PACK1_F4]], 0
135+
; KMSAN: store i32 0, ptr [[EXT1_F4]]
136+
; KMSAN: [[PACK2_F4:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
137+
; KMSAN: [[EXT2_F4:%.*]] = extractvalue { ptr, ptr } [[PACK2_F4]], 0
138+
; KMSAN: store i32 0, ptr [[EXT2_F4]]
135139

136140

137141
; One input register, three output registers:
@@ -153,15 +157,15 @@ entry:
153157
; CHECK: [[IS1_F5:%.*]] = load i32, ptr @is1, align 4
154158
; CHECK: call void @__msan_warning
155159
; CHECK: call { i32, i32, i32 } asm "",{{.*}}(i32 [[IS1_F5]])
156-
; CHECK: [[PACK1_F5:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
157-
; CHECK: [[EXT1_F5:%.*]] = extractvalue { ptr, ptr } [[PACK1_F5]], 0
158-
; CHECK: store i32 0, ptr [[EXT1_F5]]
159-
; CHECK: [[PACK2_F5:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
160-
; CHECK: [[EXT2_F5:%.*]] = extractvalue { ptr, ptr } [[PACK2_F5]], 0
161-
; CHECK: store i32 0, ptr [[EXT2_F5]]
162-
; CHECK: [[PACK3_F5:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id3{{.*}})
163-
; CHECK: [[EXT3_F5:%.*]] = extractvalue { ptr, ptr } [[PACK3_F5]], 0
164-
; CHECK: store i32 0, ptr [[EXT3_F5]]
160+
; KMSAN: [[PACK1_F5:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
161+
; KMSAN: [[EXT1_F5:%.*]] = extractvalue { ptr, ptr } [[PACK1_F5]], 0
162+
; KMSAN: store i32 0, ptr [[EXT1_F5]]
163+
; KMSAN: [[PACK2_F5:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
164+
; KMSAN: [[EXT2_F5:%.*]] = extractvalue { ptr, ptr } [[PACK2_F5]], 0
165+
; KMSAN: store i32 0, ptr [[EXT2_F5]]
166+
; KMSAN: [[PACK3_F5:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id3{{.*}})
167+
; KMSAN: [[EXT3_F5:%.*]] = extractvalue { ptr, ptr } [[PACK3_F5]], 0
168+
; KMSAN: store i32 0, ptr [[EXT3_F5]]
165169

166170

167171
; 2 input memory args, 2 output memory args:
@@ -173,6 +177,8 @@ entry:
173177
}
174178

175179
; CHECK-LABEL: @f_2i_2o_mem
180+
; USER-CONS: store i32 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @id1 to i64), i64 87960930222080) to ptr), align 1
181+
; USER-CONS: store i32 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @id2 to i64), i64 87960930222080) to ptr), align 1
176182
; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@id1{{.*}}, i64 4)
177183
; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@id2{{.*}}, i64 4)
178184
; CHECK: call void asm "", "=*m,=*m,*m,*m,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) @id1, ptr elementtype(i32) @id2, ptr elementtype(i32) @is1, ptr elementtype(i32) @is2)
@@ -190,6 +196,7 @@ entry:
190196

191197
; CHECK-LABEL: @f_1i_1o_memreg
192198
; CHECK: [[IS1_F7:%.*]] = load i32, ptr @is1, align 4
199+
; USER-CONS: store i32 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @id1 to i64), i64 87960930222080) to ptr), align 1
193200
; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@id1{{.*}}, i64 4)
194201
; CHECK: call void @__msan_warning
195202
; CHECK: call i32 asm "", "=r,=*m,r,*m,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) @id1, i32 [[IS1_F7]], ptr elementtype(i32) @is1)
@@ -208,6 +215,7 @@ entry:
208215
}
209216

210217
; CHECK-LABEL: @f_3o_reg_mem_reg
218+
; USER-CONS: store i32 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @id2 to i64), i64 87960930222080) to ptr), align 1
211219
; CHECK-CONS: call void @__msan_instrument_asm_store(ptr @id2, i64 4)
212220
; CHECK: call { i32, i32 } asm "", "=r,=*m,=r,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) @id2)
213221

@@ -232,10 +240,11 @@ entry:
232240
; CHECK: [[PAIR1_F9:%.*]] = load {{.*}} @pair1
233241
; CHECK: [[C1_F9:%.*]] = load {{.*}} @c1
234242
; CHECK: [[MEMCPY_S1_F9:%.*]] = load {{.*}} @memcpy_s1
243+
; USER-CONS: store { i32, i32 } zeroinitializer, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @pair2 to i64), i64 87960930222080) to ptr), align 1
235244
; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@pair2{{.*}}, i64 8)
236245
; CHECK: call void @__msan_warning
237-
; CHECK: call void @__msan_warning
238-
; CHECK: call void @__msan_warning
246+
; KMSAN: call void @__msan_warning
247+
; KMSAN: call void @__msan_warning
239248
; CHECK: call { i8, ptr } asm "", "=*r,=r,=r,r,r,r,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(%struct.pair) @pair2, {{.*}}[[PAIR1_F9]], i8 [[C1_F9]], {{.*}} [[MEMCPY_S1_F9]])
240249

241250
; Three inputs and three outputs of different types: a pair, a char, a function pointer.
@@ -248,9 +257,12 @@ entry:
248257
}
249258

250259
; CHECK-LABEL: @f_3i_3o_complex_mem
251-
; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@pair2{{.*}}, i64 8)
252-
; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@c2{{.*}}, i64 1)
253-
; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@memcpy_d1{{.*}}, i64 8)
260+
; USER-CONS: store { i32, i32 } zeroinitializer, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @pair2 to i64), i64 87960930222080) to ptr), align 1
261+
; USER-CONS-NEXT: store i8 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @c2 to i64), i64 87960930222080) to ptr), align 1
262+
; USER-CONS-NEXT: store i64 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @memcpy_d1 to i64), i64 87960930222080) to ptr), align 1
263+
; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@pair2{{.*}}, i64 8)
264+
; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@c2{{.*}}, i64 1)
265+
; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@memcpy_d1{{.*}}, i64 8)
254266
; CHECK: call void asm "", "=*m,=*m,=*m,*m,*m,*m,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(%struct.pair) @pair2, ptr elementtype(i8) @c2, ptr elementtype(ptr) @memcpy_d1, ptr elementtype(%struct.pair) @pair1, ptr elementtype(i8) @c1, ptr elementtype(ptr) @memcpy_s1)
255267

256268

@@ -278,8 +290,8 @@ cleanup: ; preds = %entry, %skip_label
278290
}
279291

280292
; CHECK-LABEL: @asm_goto
281-
; CHECK: [[LOAD_ARG:%.*]] = load {{.*}} %_msarg
282-
; CHECK: [[CMP:%.*]] = icmp ne {{.*}} [[LOAD_ARG]], 0
283-
; CHECK: br {{.*}} [[CMP]], label %[[LABEL:.*]], label
284-
; CHECK: [[LABEL]]:
285-
; CHECK-NEXT: call void @__msan_warning
293+
; KMSAN: [[LOAD_ARG:%.*]] = load {{.*}} %_msarg
294+
; KMSAN: [[CMP:%.*]] = icmp ne {{.*}} [[LOAD_ARG]], 0
295+
; KMSAN: br {{.*}} [[CMP]], label %[[LABEL:.*]], label
296+
; KMSAN: [[LABEL]]:
297+
; KMSAN-NEXT: call void @__msan_warning

0 commit comments

Comments
 (0)