From 5b61b3818a3aadb928485571d69e5bccc52e1d5b Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 30 Apr 2010 19:05:00 +0000 Subject: [PATCH] Add lint checks for invalid uses of memory. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@102733 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Analysis/Lint.cpp | 78 ++++++++++++++++++++++++++++++++----------- test/Other/lint.ll | 18 ++++++++++ 2 files changed, 77 insertions(+), 19 deletions(-) diff --git a/lib/Analysis/Lint.cpp b/lib/Analysis/Lint.cpp index 7c3e6b3e84b..25d4f9571da 100644 --- a/lib/Analysis/Lint.cpp +++ b/lib/Analysis/Lint.cpp @@ -51,6 +51,13 @@ using namespace llvm; namespace { + namespace MemRef { + static unsigned Read = 1; + static unsigned Write = 2; + static unsigned Callee = 4; + static unsigned Branchee = 8; + } + class Lint : public FunctionPass, public InstVisitor { friend class InstVisitor; @@ -58,7 +65,7 @@ namespace { void visitCallSite(CallSite CS); void visitMemoryReference(Instruction &I, Value *Ptr, unsigned Align, - const Type *Ty); + const Type *Ty, unsigned Flags); void visitCallInst(CallInst &I); void visitInvokeInst(InvokeInst &I); @@ -187,7 +194,7 @@ void Lint::visitCallSite(CallSite CS) { Value *Callee = CS.getCalledValue(); // TODO: Check function alignment? - visitMemoryReference(I, Callee, 0, 0); + visitMemoryReference(I, Callee, 0, 0, MemRef::Callee); if (Function *F = dyn_cast(Callee->stripPointerCasts())) { Assert1(CS.getCallingConv() == F->getCallingConv(), @@ -222,8 +229,10 @@ void Lint::visitCallSite(CallSite CS) { case Intrinsic::memcpy: { MemCpyInst *MCI = cast(&I); - visitMemoryReference(I, MCI->getSource(), MCI->getAlignment(), 0); - visitMemoryReference(I, MCI->getDest(), MCI->getAlignment(), 0); + visitMemoryReference(I, MCI->getSource(), MCI->getAlignment(), 0, + MemRef::Write); + visitMemoryReference(I, MCI->getDest(), MCI->getAlignment(), 0, + MemRef::Read); // Check that the memcpy arguments don't overlap. The AliasAnalysis API // isn't expressive enough for what we really want to do. Known partial @@ -240,13 +249,16 @@ void Lint::visitCallSite(CallSite CS) { } case Intrinsic::memmove: { MemMoveInst *MMI = cast(&I); - visitMemoryReference(I, MMI->getSource(), MMI->getAlignment(), 0); - visitMemoryReference(I, MMI->getDest(), MMI->getAlignment(), 0); + visitMemoryReference(I, MMI->getSource(), MMI->getAlignment(), 0, + MemRef::Write); + visitMemoryReference(I, MMI->getDest(), MMI->getAlignment(), 0, + MemRef::Read); break; } case Intrinsic::memset: { MemSetInst *MSI = cast(&I); - visitMemoryReference(I, MSI->getDest(), MSI->getAlignment(), 0); + visitMemoryReference(I, MSI->getDest(), MSI->getAlignment(), 0, + MemRef::Write); break; } @@ -255,18 +267,21 @@ void Lint::visitCallSite(CallSite CS) { "Undefined behavior: va_start called in a non-varargs function", &I); - visitMemoryReference(I, CS.getArgument(0), 0, 0); + visitMemoryReference(I, CS.getArgument(0), 0, 0, + MemRef::Read | MemRef::Write); break; case Intrinsic::vacopy: - visitMemoryReference(I, CS.getArgument(0), 0, 0); - visitMemoryReference(I, CS.getArgument(1), 0, 0); + visitMemoryReference(I, CS.getArgument(0), 0, 0, MemRef::Write); + visitMemoryReference(I, CS.getArgument(1), 0, 0, MemRef::Read); break; case Intrinsic::vaend: - visitMemoryReference(I, CS.getArgument(0), 0, 0); + visitMemoryReference(I, CS.getArgument(0), 0, 0, + MemRef::Read | MemRef::Write); break; case Intrinsic::stackrestore: - visitMemoryReference(I, CS.getArgument(0), 0, 0); + visitMemoryReference(I, CS.getArgument(0), 0, 0, + MemRef::Read); break; } } @@ -287,16 +302,39 @@ void Lint::visitReturnInst(ReturnInst &I) { } // TODO: Add a length argument and check that the reference is in bounds -// TODO: Add read/write/execute flags and check for writing to read-only -// memory or jumping to suspicious writeable memory void Lint::visitMemoryReference(Instruction &I, - Value *Ptr, unsigned Align, const Type *Ty) { + Value *Ptr, unsigned Align, const Type *Ty, + unsigned Flags) { Value *UnderlyingObject = Ptr->getUnderlyingObject(); Assert1(!isa(UnderlyingObject), "Undefined behavior: Null pointer dereference", &I); Assert1(!isa(UnderlyingObject), "Undefined behavior: Undef pointer dereference", &I); + if (Flags & MemRef::Write) { + if (const GlobalVariable *GV = dyn_cast(UnderlyingObject)) + Assert1(!GV->isConstant(), + "Undefined behavior: Write to read-only memory", &I); + Assert1(!isa(UnderlyingObject) && + !isa(UnderlyingObject), + "Undefined behavior: Write to text section", &I); + } + if (Flags & MemRef::Read) { + Assert1(!isa(UnderlyingObject), + "Unusual: Load from function body", &I); + Assert1(!isa(UnderlyingObject), + "Undefined behavior: Load from block address", &I); + } + if (Flags & MemRef::Callee) { + Assert1(!isa(UnderlyingObject), + "Undefined behavior: Call to block address", &I); + } + if (Flags & MemRef::Branchee) { + Assert1(!isa(UnderlyingObject) || + isa(UnderlyingObject), + "Undefined behavior: Branch to non-blockaddress", &I); + } + if (TD) { if (Align == 0 && Ty) Align = TD->getABITypeAlignment(Ty); @@ -312,12 +350,13 @@ void Lint::visitMemoryReference(Instruction &I, } void Lint::visitLoadInst(LoadInst &I) { - visitMemoryReference(I, I.getPointerOperand(), I.getAlignment(), I.getType()); + visitMemoryReference(I, I.getPointerOperand(), I.getAlignment(), I.getType(), + MemRef::Read); } void Lint::visitStoreInst(StoreInst &I) { visitMemoryReference(I, I.getPointerOperand(), I.getAlignment(), - I.getOperand(0)->getType()); + I.getOperand(0)->getType(), MemRef::Write); } void Lint::visitXor(BinaryOperator &I) { @@ -392,11 +431,12 @@ void Lint::visitAllocaInst(AllocaInst &I) { } void Lint::visitVAArgInst(VAArgInst &I) { - visitMemoryReference(I, I.getOperand(0), 0, 0); + visitMemoryReference(I, I.getOperand(0), 0, 0, + MemRef::Read | MemRef::Write); } void Lint::visitIndirectBrInst(IndirectBrInst &I) { - visitMemoryReference(I, I.getAddress(), 0, 0); + visitMemoryReference(I, I.getAddress(), 0, 0, MemRef::Branchee); } void Lint::visitExtractElementInst(ExtractElementInst &I) { diff --git a/test/Other/lint.ll b/test/Other/lint.ll index 8658495c971..d0db5e46c66 100644 --- a/test/Other/lint.ll +++ b/test/Other/lint.ll @@ -3,6 +3,8 @@ target datalayout = "e-p:64:64:64" declare fastcc void @bar() +@CG = constant i32 7 + define i32 @foo() noreturn { ; CHECK: Caller and callee calling convention differ call void @bar() @@ -39,6 +41,16 @@ define i32 @foo() noreturn { %xx = xor i32 undef, undef ; CHECK: sub(undef, undef) %xs = sub i32 undef, undef + +; CHECK: Write to read-only memory + store i32 8, i32* @CG +; CHECK: Write to text section + store i32 8, i32* bitcast (i32()* @foo to i32*) +; CHECK: Load from block address + %lb = load i32* bitcast (i8* blockaddress(@foo, %next) to i32*) +; CHECK: Call to block address + call void()* bitcast (i8* blockaddress(@foo, %next) to void()*)() + br label %next next: @@ -64,3 +76,9 @@ define void @not_vararg(i8* %p) nounwind { call void @llvm.va_start(i8* %p) ret void } + +define void @use_indbr() { + indirectbr i8* bitcast (i32()* @foo to i8*), [label %block] +block: + unreachable +} -- 2.34.1