Add support for calloc() in MallocChecker. Patch by Jordy Rose, with my
modification. llvm-svn: 105264
This commit is contained in:
parent
1a764f93a0
commit
527ff6d1dc
|
@ -211,6 +211,8 @@ public:
|
|||
|
||||
const GRState *bindLoc(SVal location, SVal V) const;
|
||||
|
||||
const GRState *bindDefault(SVal loc, SVal V) const;
|
||||
|
||||
const GRState *unbindLoc(Loc LV) const;
|
||||
|
||||
/// Get the lvalue for a variable reference.
|
||||
|
@ -620,6 +622,12 @@ inline const GRState *GRState::bindLoc(SVal LV, SVal V) const {
|
|||
return !isa<Loc>(LV) ? this : bindLoc(cast<Loc>(LV), V);
|
||||
}
|
||||
|
||||
inline const GRState *GRState::bindDefault(SVal loc, SVal V) const {
|
||||
const MemRegion *R = cast<loc::MemRegionVal>(loc).getRegion();
|
||||
Store new_store = getStateManager().StoreMgr->BindDefault(St, R, V);
|
||||
return makeWithStore(new_store);
|
||||
}
|
||||
|
||||
inline SVal GRState::getLValue(const VarDecl* VD,
|
||||
const LocationContext *LC) const {
|
||||
return getStateManager().StoreMgr->getLValueVar(VD, LC);
|
||||
|
|
|
@ -64,6 +64,10 @@ public:
|
|||
/// to the location given for \c loc.
|
||||
virtual Store Bind(Store store, Loc loc, SVal val) = 0;
|
||||
|
||||
virtual Store BindDefault(Store store, const MemRegion *R, SVal V) {
|
||||
return store;
|
||||
}
|
||||
|
||||
virtual Store Remove(Store St, Loc L) = 0;
|
||||
|
||||
/// BindCompoundLiteral - Return the store that has the bindings currently
|
||||
|
|
|
@ -59,12 +59,12 @@ class MallocChecker : public CheckerVisitor<MallocChecker> {
|
|||
BuiltinBug *BT_DoubleFree;
|
||||
BuiltinBug *BT_Leak;
|
||||
BuiltinBug *BT_UseFree;
|
||||
IdentifierInfo *II_malloc, *II_free, *II_realloc;
|
||||
IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc;
|
||||
|
||||
public:
|
||||
MallocChecker()
|
||||
: BT_DoubleFree(0), BT_Leak(0), BT_UseFree(0),
|
||||
II_malloc(0), II_free(0), II_realloc(0) {}
|
||||
II_malloc(0), II_free(0), II_realloc(0), II_calloc(0) {}
|
||||
static void *getTag();
|
||||
bool EvalCallExpr(CheckerContext &C, const CallExpr *CE);
|
||||
void EvalDeadSymbols(CheckerContext &C,const Stmt *S,SymbolReaper &SymReaper);
|
||||
|
@ -76,12 +76,20 @@ public:
|
|||
private:
|
||||
void MallocMem(CheckerContext &C, const CallExpr *CE);
|
||||
const GRState *MallocMemAux(CheckerContext &C, const CallExpr *CE,
|
||||
const Expr *SizeEx, const GRState *state);
|
||||
const Expr *SizeEx, SVal Init,
|
||||
const GRState *state) {
|
||||
return MallocMemAux(C, CE, state->getSVal(SizeEx), Init, state);
|
||||
}
|
||||
const GRState *MallocMemAux(CheckerContext &C, const CallExpr *CE,
|
||||
SVal SizeEx, SVal Init,
|
||||
const GRState *state);
|
||||
|
||||
void FreeMem(CheckerContext &C, const CallExpr *CE);
|
||||
const GRState *FreeMemAux(CheckerContext &C, const CallExpr *CE,
|
||||
const GRState *state);
|
||||
|
||||
void ReallocMem(CheckerContext &C, const CallExpr *CE);
|
||||
void CallocMem(CheckerContext &C, const CallExpr *CE);
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
|
@ -120,6 +128,8 @@ bool MallocChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) {
|
|||
II_free = &Ctx.Idents.get("free");
|
||||
if (!II_realloc)
|
||||
II_realloc = &Ctx.Idents.get("realloc");
|
||||
if (!II_calloc)
|
||||
II_calloc = &Ctx.Idents.get("calloc");
|
||||
|
||||
if (FD->getIdentifier() == II_malloc) {
|
||||
MallocMem(C, CE);
|
||||
|
@ -136,28 +146,34 @@ bool MallocChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (FD->getIdentifier() == II_calloc) {
|
||||
CallocMem(C, CE);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MallocChecker::MallocMem(CheckerContext &C, const CallExpr *CE) {
|
||||
const GRState *state = MallocMemAux(C, CE, CE->getArg(0), C.getState());
|
||||
const GRState *state = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(),
|
||||
C.getState());
|
||||
C.addTransition(state);
|
||||
}
|
||||
|
||||
const GRState *MallocChecker::MallocMemAux(CheckerContext &C,
|
||||
const CallExpr *CE,
|
||||
const Expr *SizeEx,
|
||||
SVal Size, SVal Init,
|
||||
const GRState *state) {
|
||||
unsigned Count = C.getNodeBuilder().getCurrentBlockCount();
|
||||
ValueManager &ValMgr = C.getValueManager();
|
||||
|
||||
SVal RetVal = ValMgr.getConjuredSymbolVal(NULL, CE, CE->getType(), Count);
|
||||
|
||||
SVal Size = state->getSVal(SizeEx);
|
||||
|
||||
state = C.getEngine().getStoreManager().setExtent(state, RetVal.getAsRegion(),
|
||||
Size);
|
||||
|
||||
state = state->bindDefault(RetVal, Init);
|
||||
|
||||
state = state->BindExpr(CE, RetVal);
|
||||
|
||||
SymbolRef Sym = RetVal.getAsLocSymbol();
|
||||
|
@ -234,7 +250,8 @@ void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) {
|
|||
if (Sym)
|
||||
stateEqual = stateEqual->set<RegionState>(Sym, RefState::getReleased(CE));
|
||||
|
||||
const GRState *stateMalloc = MallocMemAux(C, CE, CE->getArg(1), stateEqual);
|
||||
const GRState *stateMalloc = MallocMemAux(C, CE, CE->getArg(1),
|
||||
UndefinedVal(), stateEqual);
|
||||
C.addTransition(stateMalloc);
|
||||
}
|
||||
|
||||
|
@ -256,13 +273,30 @@ void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) {
|
|||
if (stateFree) {
|
||||
// FIXME: We should copy the content of the original buffer.
|
||||
const GRState *stateRealloc = MallocMemAux(C, CE, CE->getArg(1),
|
||||
stateFree);
|
||||
UnknownVal(), stateFree);
|
||||
C.addTransition(stateRealloc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE) {
|
||||
const GRState *state = C.getState();
|
||||
|
||||
ValueManager &ValMgr = C.getValueManager();
|
||||
SValuator &SVator = C.getSValuator();
|
||||
|
||||
SVal Count = state->getSVal(CE->getArg(0));
|
||||
SVal EleSize = state->getSVal(CE->getArg(1));
|
||||
SVal TotalSize = SVator.EvalBinOp(state, BinaryOperator::Mul, Count, EleSize,
|
||||
ValMgr.getContext().getSizeType());
|
||||
|
||||
SVal Zero = ValMgr.makeZeroVal(ValMgr.getContext().CharTy);
|
||||
|
||||
state = MallocMemAux(C, CE, TotalSize, Zero, state);
|
||||
C.addTransition(state);
|
||||
}
|
||||
|
||||
void MallocChecker::EvalDeadSymbols(CheckerContext &C, const Stmt *S,
|
||||
SymbolReaper &SymReaper) {
|
||||
for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(),
|
||||
|
|
|
@ -280,6 +280,10 @@ public: // Part of public interface to class.
|
|||
|
||||
Store Bind(Store store, Loc LV, SVal V);
|
||||
|
||||
Store BindDefault(Store store, const MemRegion *R, SVal V) {
|
||||
return Add(GetRegionBindings(store), R, BindingKey::Default, V).getRoot();
|
||||
}
|
||||
|
||||
Store BindCompoundLiteral(Store store, const CompoundLiteralExpr* CL,
|
||||
const LocationContext *LC, SVal V);
|
||||
|
||||
|
@ -1233,7 +1237,7 @@ SVal RegionStoreManager::RetrieveFieldOrElementCommon(Store store,
|
|||
if (D->isZeroConstant())
|
||||
return ValMgr.makeZeroVal(Ty);
|
||||
|
||||
if (D->isUnknown())
|
||||
if (D->isUnknownOrUndef())
|
||||
return *D;
|
||||
|
||||
assert(0 && "Unknown default value");
|
||||
|
@ -1456,6 +1460,7 @@ Store RegionStoreManager::BindCompoundLiteral(Store store,
|
|||
V);
|
||||
}
|
||||
|
||||
|
||||
Store RegionStoreManager::setImplicitDefaultValue(Store store,
|
||||
const MemRegion *R,
|
||||
QualType T) {
|
||||
|
|
|
@ -77,3 +77,35 @@ void PR7217() {
|
|||
buf[1] = 'c'; // not crash
|
||||
|
||||
}
|
||||
// This tests that malloc() buffers are undefined by default
|
||||
char mallocGarbage () {
|
||||
char *buf = malloc(2);
|
||||
char result = buf[1]; // expected-warning{{undefined}}
|
||||
free(buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
// This tests that calloc() buffers need to be freed
|
||||
void callocNoFree () {
|
||||
char *buf = calloc(2,2);
|
||||
return; // expected-warning{{never released}}
|
||||
}
|
||||
|
||||
// These test that calloc() buffers are zeroed by default
|
||||
char callocZeroesGood () {
|
||||
char *buf = calloc(2,2);
|
||||
char result = buf[3]; // no-warning
|
||||
if (buf[1] == 0) {
|
||||
free(buf);
|
||||
}
|
||||
return result; // no-warning
|
||||
}
|
||||
|
||||
char callocZeroesBad () {
|
||||
char *buf = calloc(2,2);
|
||||
char result = buf[3]; // no-warning
|
||||
if (buf[1] != 0) {
|
||||
free(buf);
|
||||
}
|
||||
return result; // expected-warning{{never released}}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
typedef __typeof(sizeof(int)) size_t;
|
||||
void *malloc(size_t);
|
||||
void *calloc(size_t, size_t);
|
||||
|
||||
char f1() {
|
||||
char* s = "abcd";
|
||||
|
@ -36,3 +37,9 @@ void f4() {
|
|||
p[1] = a; // no-warning
|
||||
p[2] = a; // expected-warning{{Access out-of-bound array element (buffer overflow)}}
|
||||
}
|
||||
|
||||
void f5() {
|
||||
char *p = calloc(2,2);
|
||||
p[3] = '.'; // no-warning
|
||||
p[4] = '!'; // expected-warning{{out-of-bound}}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
// RUN: %clang_cc1 -analyze -analyzer-experimental-internal-checks -analyzer-check-objc-mem -analyzer-experimental-checks -analyzer-store=region -verify %s
|
||||
typedef __typeof(sizeof(int)) size_t;
|
||||
void *malloc(size_t);
|
||||
void free(void *);
|
||||
|
||||
char stackBased1 () {
|
||||
char buf[2];
|
||||
buf[0] = 'a';
|
||||
return buf[1]; // expected-warning{{Undefined}}
|
||||
}
|
||||
|
||||
char stackBased2 () {
|
||||
char buf[2];
|
||||
buf[1] = 'a';
|
||||
return buf[0]; // expected-warning{{Undefined}}
|
||||
}
|
||||
|
||||
char heapBased1 () {
|
||||
char *buf = malloc(2);
|
||||
buf[0] = 'a';
|
||||
char result = buf[1]; // expected-warning{{undefined}}
|
||||
free(buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
char heapBased2 () {
|
||||
char *buf = malloc(2);
|
||||
buf[1] = 'a';
|
||||
char result = buf[0]; // expected-warning{{undefined}}
|
||||
free(buf);
|
||||
return result;
|
||||
}
|
Loading…
Reference in New Issue