From c7d7e0cbe0a83881e4a01b0be745e169bd1baea0 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 8 May 2010 22:15:59 +0000 Subject: [PATCH] make simplifycfg insert an llvm.trap before the 'unreachable' it introduces when it detects undefined behavior. llvm.trap generally codegens into some thing really small (e.g. a 2 byte ud2 instruction on x86) and debugging this sort of thing is "nontrivial". For example, we now compile: void foo() { *(int*)0 = 42; } into: _foo: pushl %ebp movl %esp, %ebp ud2 Some may even claim that this is a security hole, though that seems dubious to me. This addresses rdar://7958343 - Optimizing away null dereference potentially allows arbitrary code execution git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@103356 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Transforms/Scalar/SimplifyCFGPass.cpp | 14 ++++++++++--- .../SimplifyCFG/trapping-load-unreachable.ll | 20 ++++++++++++++++--- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/lib/Transforms/Scalar/SimplifyCFGPass.cpp b/lib/Transforms/Scalar/SimplifyCFGPass.cpp index b621e8d8aa6..97441008c46 100644 --- a/lib/Transforms/Scalar/SimplifyCFGPass.cpp +++ b/lib/Transforms/Scalar/SimplifyCFGPass.cpp @@ -58,13 +58,20 @@ FunctionPass *llvm::createCFGSimplificationPass() { /// ChangeToUnreachable - Insert an unreachable instruction before the specified /// instruction, making it and the rest of the code in the block dead. -static void ChangeToUnreachable(Instruction *I) { +static void ChangeToUnreachable(Instruction *I, bool UseLLVMTrap) { BasicBlock *BB = I->getParent(); // Loop over all of the successors, removing BB's entry from any PHI // nodes. for (succ_iterator SI = succ_begin(BB), SE = succ_end(BB); SI != SE; ++SI) (*SI)->removePredecessor(BB); + // Insert a call to llvm.trap right before this. This turns the undefined + // behavior into a hard fail instead of falling through into random code. + if (UseLLVMTrap) { + Function *TrapFn = + Intrinsic::getDeclaration(BB->getParent()->getParent(), Intrinsic::trap); + CallInst::Create(TrapFn, "", I); + } new UnreachableInst(I->getContext(), I); // All instructions after this are dead. @@ -118,7 +125,8 @@ static bool MarkAliveBlocks(BasicBlock *BB, // though. ++BBI; if (!isa(BBI)) { - ChangeToUnreachable(BBI); + // Don't insert a call to llvm.trap right before the unreachable. + ChangeToUnreachable(BBI, false); Changed = true; } break; @@ -134,7 +142,7 @@ static bool MarkAliveBlocks(BasicBlock *BB, if (isa(Ptr) || (isa(Ptr) && SI->getPointerAddressSpace() == 0)) { - ChangeToUnreachable(SI); + ChangeToUnreachable(SI, true); Changed = true; break; } diff --git a/test/Transforms/SimplifyCFG/trapping-load-unreachable.ll b/test/Transforms/SimplifyCFG/trapping-load-unreachable.ll index 0c9cc8be925..6956faabc33 100644 --- a/test/Transforms/SimplifyCFG/trapping-load-unreachable.ll +++ b/test/Transforms/SimplifyCFG/trapping-load-unreachable.ll @@ -1,19 +1,33 @@ -; RUN: opt < %s -simplifycfg -S | grep {volatile load} +; RUN: opt < %s -simplifycfg -S | FileCheck %s ; PR2967 target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32" target triple = "i386-pc-linux-gnu" -define void @foo(i32 %x) nounwind { +define void @test1(i32 %x) nounwind { entry: %0 = icmp eq i32 %x, 0 ; [#uses=1] br i1 %0, label %bb, label %return bb: ; preds = %entry - %1 = volatile load i32* null ; [#uses=0] + %1 = volatile load i32* null unreachable + br label %return return: ; preds = %entry ret void +; CHECK: @test1 +; CHECK: volatile load +} + +; rdar://7958343 +define void @test2() nounwind { +entry: + store i32 4,i32* null + ret void + +; CHECK: @test2 +; CHECK: call void @llvm.trap +; CHECK: unreachable }