[llvm] [msan] Unpoison indirect outputs for userspace when -msan-handle-asm-conservative is specified (PR #77393)
Fangrui Song via llvm-commits
llvm-commits at lists.llvm.org
Mon Jan 8 15:22:31 PST 2024
https://github.com/MaskRay created https://github.com/llvm/llvm-project/pull/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: https://github.com/google/sanitizers/issues/192
>From 93ab63157297553fa678752995773db8b14642fe Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Mon, 8 Jan 2024 15:22:21 -0800
Subject: [PATCH] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20initia?=
=?UTF-8?q?l=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Created using spr 1.3.4
---
.../Instrumentation/MemorySanitizer.cpp | 14 ++-
.../MemorySanitizer/msan_asm_conservative.ll | 106 ++++++++++--------
2 files changed, 71 insertions(+), 49 deletions(-)
diff --git a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp
index 94af63da38c82c..38270716bc6996 100644
--- a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp
@@ -4103,7 +4103,11 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
// do the usual thing: check argument shadow and mark all outputs as
// clean. Note that any side effects of the inline asm that are not
// immediately visible in its constraints are not handled.
- if (ClHandleAsmConservative && MS.CompileKernel)
+ // For now, handle inline asm by default for KMSAN.
+ bool HandleAsm = ClHandleAsmConservative.getNumOccurrences()
+ ? ClHandleAsmConservative
+ : MS.CompileKernel;
+ if (HandleAsm)
visitAsmInstruction(CB);
else
visitInstruction(CB);
@@ -4557,7 +4561,13 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
return;
Value *SizeVal =
IRB.CreateTypeSize(MS.IntptrTy, DL.getTypeStoreSize(ElemTy));
- IRB.CreateCall(MS.MsanInstrumentAsmStoreFn, {Operand, SizeVal});
+ if (MS.CompileKernel) {
+ IRB.CreateCall(MS.MsanInstrumentAsmStoreFn, {Operand, SizeVal});
+ } else {
+ auto [ShadowPtr, _] =
+ getShadowOriginPtrUserspace(Operand, IRB, IRB.getInt8Ty(), Align(1));
+ IRB.CreateAlignedStore(getCleanShadow(ElemTy), ShadowPtr, Align(1));
+ }
}
/// Get the number of output arguments returned by pointers.
diff --git a/llvm/test/Instrumentation/MemorySanitizer/msan_asm_conservative.ll b/llvm/test/Instrumentation/MemorySanitizer/msan_asm_conservative.ll
index 1239698f3ac326..9a501ee6954c9c 100644
--- a/llvm/test/Instrumentation/MemorySanitizer/msan_asm_conservative.ll
+++ b/llvm/test/Instrumentation/MemorySanitizer/msan_asm_conservative.ll
@@ -1,10 +1,14 @@
; Test for handling of asm constraints in MSan instrumentation.
+; RUN: opt < %s -msan-check-access-address=0 -msan-handle-asm-conservative=0 -S -passes=msan 2>&1 | \
+; RUN: FileCheck %s
+; RUN: opt < %s -msan-check-access-address=0 -msan-handle-asm-conservative=1 -S -passes=msan 2>&1 | \
+; RUN: FileCheck --check-prefixes=CHECK,USER-CONS %s
; RUN: opt < %s -msan-kernel=1 -msan-check-access-address=0 \
-; RUN: -msan-handle-asm-conservative=0 -S -passes=msan 2>&1 | FileCheck \
-; RUN: "-check-prefix=CHECK" %s
+; RUN: -msan-handle-asm-conservative=0 -S -passes=msan 2>&1 | FileCheck \
+; RUN: --check-prefixes=CHECK,KMSAN %s
; RUN: opt < %s -msan-kernel=1 -msan-check-access-address=0 \
-; RUN: -msan-handle-asm-conservative=1 -S -passes=msan 2>&1 | FileCheck \
-; RUN: "-check-prefixes=CHECK,CHECK-CONS" %s
+; RUN: -msan-handle-asm-conservative=1 -S -passes=msan 2>&1 | FileCheck \
+; RUN: --check-prefixes=CHECK,KMSAN,CHECK-CONS %s
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@@ -46,9 +50,9 @@ entry:
; CHECK: [[IS1_F1:%.*]] = load i32, ptr @is1, align 4
; CHECK: call void @__msan_warning
; CHECK: call i32 asm "",{{.*}}(i32 [[IS1_F1]])
-; CHECK: [[PACK1_F1:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
-; CHECK: [[EXT1_F1:%.*]] = extractvalue { ptr, ptr } [[PACK1_F1]], 0
-; CHECK: store i32 0, ptr [[EXT1_F1]]
+; KMSAN: [[PACK1_F1:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
+; KMSAN: [[EXT1_F1:%.*]] = extractvalue { ptr, ptr } [[PACK1_F1]], 0
+; KMSAN: store i32 0, ptr [[EXT1_F1]]
; Two input registers, two output registers:
@@ -69,14 +73,14 @@ entry:
; CHECK: [[IS1_F2:%.*]] = load i32, ptr @is1, align 4
; CHECK: [[IS2_F2:%.*]] = load i32, ptr @is2, align 4
; CHECK: call void @__msan_warning
-; CHECK: call void @__msan_warning
+; KMSAN: call void @__msan_warning
; CHECK: call { i32, i32 } asm "",{{.*}}(i32 [[IS1_F2]], i32 [[IS2_F2]])
-; CHECK: [[PACK1_F2:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
-; CHECK: [[EXT1_F2:%.*]] = extractvalue { ptr, ptr } [[PACK1_F2]], 0
-; CHECK: store i32 0, ptr [[EXT1_F2]]
-; CHECK: [[PACK2_F2:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
-; CHECK: [[EXT2_F2:%.*]] = extractvalue { ptr, ptr } [[PACK2_F2]], 0
-; CHECK: store i32 0, ptr [[EXT2_F2]]
+; KMSAN: [[PACK1_F2:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
+; KMSAN: [[EXT1_F2:%.*]] = extractvalue { ptr, ptr } [[PACK1_F2]], 0
+; KMSAN: store i32 0, ptr [[EXT1_F2]]
+; KMSAN: [[PACK2_F2:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
+; KMSAN: [[EXT2_F2:%.*]] = extractvalue { ptr, ptr } [[PACK2_F2]], 0
+; KMSAN: store i32 0, ptr [[EXT2_F2]]
; Input same as output, used twice:
; asm("" : "=r" (id1), "=r" (id2) : "r" (id1), "r" (id2));
@@ -96,14 +100,14 @@ entry:
; CHECK: [[ID1_F3:%.*]] = load i32, ptr @id1, align 4
; CHECK: [[ID2_F3:%.*]] = load i32, ptr @id2, align 4
; CHECK: call void @__msan_warning
-; CHECK: call void @__msan_warning
+; KMSAN: call void @__msan_warning
; CHECK: call { i32, i32 } asm "",{{.*}}(i32 [[ID1_F3]], i32 [[ID2_F3]])
-; CHECK: [[PACK1_F3:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
-; CHECK: [[EXT1_F3:%.*]] = extractvalue { ptr, ptr } [[PACK1_F3]], 0
-; CHECK: store i32 0, ptr [[EXT1_F3]]
-; CHECK: [[PACK2_F3:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
-; CHECK: [[EXT2_F3:%.*]] = extractvalue { ptr, ptr } [[PACK2_F3]], 0
-; CHECK: store i32 0, ptr [[EXT2_F3]]
+; KMSAN: [[PACK1_F3:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
+; KMSAN: [[EXT1_F3:%.*]] = extractvalue { ptr, ptr } [[PACK1_F3]], 0
+; KMSAN: store i32 0, ptr [[EXT1_F3]]
+; KMSAN: [[PACK2_F3:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
+; KMSAN: [[EXT2_F3:%.*]] = extractvalue { ptr, ptr } [[PACK2_F3]], 0
+; KMSAN: store i32 0, ptr [[EXT2_F3]]
; One of the input registers is also an output:
@@ -124,14 +128,14 @@ entry:
; CHECK: [[ID1_F4:%.*]] = load i32, ptr @id1, align 4
; CHECK: [[IS1_F4:%.*]] = load i32, ptr @is1, align 4
; CHECK: call void @__msan_warning
-; CHECK: call void @__msan_warning
+; KMSAN: call void @__msan_warning
; CHECK: call { i32, i32 } asm "",{{.*}}(i32 [[ID1_F4]], i32 [[IS1_F4]])
-; CHECK: [[PACK1_F4:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
-; CHECK: [[EXT1_F4:%.*]] = extractvalue { ptr, ptr } [[PACK1_F4]], 0
-; CHECK: store i32 0, ptr [[EXT1_F4]]
-; CHECK: [[PACK2_F4:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
-; CHECK: [[EXT2_F4:%.*]] = extractvalue { ptr, ptr } [[PACK2_F4]], 0
-; CHECK: store i32 0, ptr [[EXT2_F4]]
+; KMSAN: [[PACK1_F4:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
+; KMSAN: [[EXT1_F4:%.*]] = extractvalue { ptr, ptr } [[PACK1_F4]], 0
+; KMSAN: store i32 0, ptr [[EXT1_F4]]
+; KMSAN: [[PACK2_F4:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
+; KMSAN: [[EXT2_F4:%.*]] = extractvalue { ptr, ptr } [[PACK2_F4]], 0
+; KMSAN: store i32 0, ptr [[EXT2_F4]]
; One input register, three output registers:
@@ -153,15 +157,15 @@ entry:
; CHECK: [[IS1_F5:%.*]] = load i32, ptr @is1, align 4
; CHECK: call void @__msan_warning
; CHECK: call { i32, i32, i32 } asm "",{{.*}}(i32 [[IS1_F5]])
-; CHECK: [[PACK1_F5:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
-; CHECK: [[EXT1_F5:%.*]] = extractvalue { ptr, ptr } [[PACK1_F5]], 0
-; CHECK: store i32 0, ptr [[EXT1_F5]]
-; CHECK: [[PACK2_F5:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
-; CHECK: [[EXT2_F5:%.*]] = extractvalue { ptr, ptr } [[PACK2_F5]], 0
-; CHECK: store i32 0, ptr [[EXT2_F5]]
-; CHECK: [[PACK3_F5:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id3{{.*}})
-; CHECK: [[EXT3_F5:%.*]] = extractvalue { ptr, ptr } [[PACK3_F5]], 0
-; CHECK: store i32 0, ptr [[EXT3_F5]]
+; KMSAN: [[PACK1_F5:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
+; KMSAN: [[EXT1_F5:%.*]] = extractvalue { ptr, ptr } [[PACK1_F5]], 0
+; KMSAN: store i32 0, ptr [[EXT1_F5]]
+; KMSAN: [[PACK2_F5:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
+; KMSAN: [[EXT2_F5:%.*]] = extractvalue { ptr, ptr } [[PACK2_F5]], 0
+; KMSAN: store i32 0, ptr [[EXT2_F5]]
+; KMSAN: [[PACK3_F5:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id3{{.*}})
+; KMSAN: [[EXT3_F5:%.*]] = extractvalue { ptr, ptr } [[PACK3_F5]], 0
+; KMSAN: store i32 0, ptr [[EXT3_F5]]
; 2 input memory args, 2 output memory args:
@@ -173,6 +177,8 @@ entry:
}
; CHECK-LABEL: @f_2i_2o_mem
+; USER-CONS: store i32 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @id1 to i64), i64 87960930222080) to ptr), align 1
+; USER-CONS: store i32 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @id2 to i64), i64 87960930222080) to ptr), align 1
; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@id1{{.*}}, i64 4)
; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@id2{{.*}}, i64 4)
; 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:
; CHECK-LABEL: @f_1i_1o_memreg
; CHECK: [[IS1_F7:%.*]] = load i32, ptr @is1, align 4
+; USER-CONS: store i32 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @id1 to i64), i64 87960930222080) to ptr), align 1
; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@id1{{.*}}, i64 4)
; CHECK: call void @__msan_warning
; 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:
}
; CHECK-LABEL: @f_3o_reg_mem_reg
+; USER-CONS: store i32 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @id2 to i64), i64 87960930222080) to ptr), align 1
; CHECK-CONS: call void @__msan_instrument_asm_store(ptr @id2, i64 4)
; CHECK: call { i32, i32 } asm "", "=r,=*m,=r,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) @id2)
@@ -232,10 +240,11 @@ entry:
; CHECK: [[PAIR1_F9:%.*]] = load {{.*}} @pair1
; CHECK: [[C1_F9:%.*]] = load {{.*}} @c1
; CHECK: [[MEMCPY_S1_F9:%.*]] = load {{.*}} @memcpy_s1
+; USER-CONS: store { i32, i32 } zeroinitializer, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @pair2 to i64), i64 87960930222080) to ptr), align 1
; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@pair2{{.*}}, i64 8)
; CHECK: call void @__msan_warning
-; CHECK: call void @__msan_warning
-; CHECK: call void @__msan_warning
+; KMSAN: call void @__msan_warning
+; KMSAN: call void @__msan_warning
; 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]])
; Three inputs and three outputs of different types: a pair, a char, a function pointer.
@@ -248,9 +257,12 @@ entry:
}
; CHECK-LABEL: @f_3i_3o_complex_mem
-; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@pair2{{.*}}, i64 8)
-; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@c2{{.*}}, i64 1)
-; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@memcpy_d1{{.*}}, i64 8)
+; USER-CONS: store { i32, i32 } zeroinitializer, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @pair2 to i64), i64 87960930222080) to ptr), align 1
+; USER-CONS-NEXT: store i8 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @c2 to i64), i64 87960930222080) to ptr), align 1
+; USER-CONS-NEXT: store i64 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @memcpy_d1 to i64), i64 87960930222080) to ptr), align 1
+; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@pair2{{.*}}, i64 8)
+; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@c2{{.*}}, i64 1)
+; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@memcpy_d1{{.*}}, i64 8)
; 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)
@@ -278,8 +290,8 @@ cleanup: ; preds = %entry, %skip_label
}
; CHECK-LABEL: @asm_goto
-; CHECK: [[LOAD_ARG:%.*]] = load {{.*}} %_msarg
-; CHECK: [[CMP:%.*]] = icmp ne {{.*}} [[LOAD_ARG]], 0
-; CHECK: br {{.*}} [[CMP]], label %[[LABEL:.*]], label
-; CHECK: [[LABEL]]:
-; CHECK-NEXT: call void @__msan_warning
+; KMSAN: [[LOAD_ARG:%.*]] = load {{.*}} %_msarg
+; KMSAN: [[CMP:%.*]] = icmp ne {{.*}} [[LOAD_ARG]], 0
+; KMSAN: br {{.*}} [[CMP]], label %[[LABEL:.*]], label
+; KMSAN: [[LABEL]]:
+; KMSAN-NEXT: call void @__msan_warning
More information about the llvm-commits
mailing list