llvm.org GIT mirror llvm / 4150c1d
MSan: handle callbr instructions Summary: Handling callbr is very similar to handling an inline assembly call: MSan must checks the instruction's inputs. callbr doesn't (yet) have outputs, so there's nothing to unpoison, and conservative assembly handling doesn't apply either. Fixes PR42479. Reviewers: eugenis Subscribers: hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D64072 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@365008 91177308-0d34-0410-b5e6-96231b3b80d8 Alexander Potapenko 3 months ago
2 changed file(s) with 53 addition(s) and 22 deletion(s). Raw diff Collapse all Expand all
32323232 void visitCallSite(CallSite CS) {
32333233 Instruction &I = *CS.getInstruction();
32343234 assert(!I.getMetadata("nosanitize"));
3235 assert((CS.isCall() || CS.isInvoke()) && "Unknown type of CallSite");
3235 assert((CS.isCall() || CS.isInvoke() || CS.isCallBr()) &&
3236 "Unknown type of CallSite");
3237 if (CS.isCallBr() || (CS.isCall() && cast(&I)->isInlineAsm())) {
3238 // For inline asm (either a call to asm function, or callbr instruction),
3239 // do the usual thing: check argument shadow and mark all outputs as
3240 // clean. Note that any side effects of the inline asm that are not
3241 // immediately visible in its constraints are not handled.
3242 if (ClHandleAsmConservative && MS.CompileKernel)
3243 visitAsmInstruction(I);
3244 else
3245 visitInstruction(I);
3246 return;
3247 }
32363248 if (CS.isCall()) {
32373249 CallInst *Call = cast(&I);
3238
3239 // For inline asm, do the usual thing: check argument shadow and mark all
3240 // outputs as clean. Note that any side effects of the inline asm that are
3241 // not immediately visible in its constraints are not handled.
3242 if (Call->isInlineAsm()) {
3243 if (ClHandleAsmConservative && MS.CompileKernel)
3244 visitAsmInstruction(I);
3245 else
3246 visitInstruction(I);
3247 return;
3248 }
3249
32503250 assert(!isa(&I) && "intrinsics are handled elsewhere");
32513251
32523252 // We are going to insert code that relies on the fact that the callee
36233623 }
36243624
36253625 /// Get the number of output arguments returned by pointers.
3626 int getNumOutputArgs(InlineAsm *IA, CallInst *CI) {
3626 int getNumOutputArgs(InlineAsm *IA, CallBase *CB) {
36273627 int NumRetOutputs = 0;
36283628 int NumOutputs = 0;
3629 Type *RetTy = dyn_cast(CI)->getType();
3629 Type *RetTy = dyn_cast(CB)->getType();
36303630 if (!RetTy->isVoidTy()) {
36313631 // Register outputs are returned via the CallInst return value.
36323632 StructType *ST = dyn_cast_or_null(RetTy);
36663666 // corresponding CallInst has nO+nI+1 operands (the last operand is the
36673667 // function to be called).
36683668 const DataLayout &DL = F.getParent()->getDataLayout();
3669 CallInst *CI = dyn_cast(&I);
3670 IRBuilder<> IRB(&I);
3671 InlineAsm *IA = cast(CI->getCalledValue());
3672 int OutputArgs = getNumOutputArgs(IA, CI);
3669 CallBase *CB = dyn_cast(&I);
3670 IRBuilder<> IRB(&I);
3671 InlineAsm *IA = cast(CB->getCalledValue());
3672 int OutputArgs = getNumOutputArgs(IA, CB);
36733673 // The last operand of a CallInst is the function itself.
3674 int NumOperands = CI->getNumOperands() - 1;
3674 int NumOperands = CB->getNumOperands() - 1;
36753675
36763676 // Check input arguments. Doing so before unpoisoning output arguments, so
36773677 // that we won't overwrite uninit values before checking them.
36783678 for (int i = OutputArgs; i < NumOperands; i++) {
3679 Value *Operand = CI->getOperand(i);
3679 Value *Operand = CB->getOperand(i);
36803680 instrumentAsmArgument(Operand, I, IRB, DL, /*isOutput*/ false);
36813681 }
36823682 // Unpoison output arguments. This must happen before the actual InlineAsm
36833683 // call, so that the shadow for memory published in the asm() statement
36843684 // remains valid.
36853685 for (int i = 0; i < OutputArgs; i++) {
3686 Value *Operand = CI->getOperand(i);
3686 Value *Operand = CB->getOperand(i);
36873687 instrumentAsmArgument(Operand, I, IRB, DL, /*isOutput*/ true);
36883688 }
36893689
263263 ; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@c2{{.*}}, i64 1)
264264 ; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@memcpy_d1{{.*}}, i64 8)
265265 ; CHECK: call void asm "", "=*m,=*m,=*m,*m,*m,*m,~{dirflag},~{fpsr},~{flags}"(%struct.pair* @pair2, i8* @c2, i8* (i8*, i8*, i32)** @memcpy_d1, %struct.pair* @pair1, i8* @c1, i8* (i8*, i8*, i32)** @memcpy_s1)
266
267
268 ; A simple asm goto construct to check that callbr is handled correctly:
269 ; int asm_goto(int n) {
270 ; int v = 1;
271 ; asm goto("cmp %0, %1; jnz %l2;" :: "r"(n), "r"(v)::skip_label);
272 ; return 0;
273 ; skip_label:
274 ; return 1;
275 ; }
276 ; asm goto statements can't have outputs, so just make sure we check the input
277 ; and the compiler doesn't crash.
278 define dso_local i32 @asm_goto(i32 %n) sanitize_memory {
279 entry:
280 callbr void asm sideeffect "cmp $0, $1; jnz ${2:l}", "r,r,X,~{dirflag},~{fpsr},~{flags}"(i32 %n, i32 1, i8* blockaddress(@asm_goto, %skip_label))
281 to label %cleanup [label %skip_label]
282
283 skip_label: ; preds = %entry
284 br label %cleanup
285
286 cleanup: ; preds = %entry, %skip_label
287 %retval.0 = phi i32 [ 2, %skip_label ], [ 1, %entry ]
288 ret i32 %retval.0
289 }
290
291 ; CHECK-LABEL: @asm_goto
292 ; CHECK: [[LOAD_ARG:%.*]] = load {{.*}} %_msarg
293 ; CHECK: [[CMP:%.*]] = icmp ne {{.*}} [[LOAD_ARG]], 0
294 ; CHECK: br {{.*}} [[CMP]], label %[[LABEL:.*]], label
295 ; CHECK: [[LABEL]]:
296 ; CHECK-NEXT: call void @__msan_warning