[analyzer] Teach the MallocChecker about Glib API for two arguments
Reviewers: zaks.anna, NoQ, danielmarjamaki Reviewed By: zaks.anna, NoQ, danielmarjamaki Subscribers: cfe-commits, kalev, pwithnall Differential Revision: https://reviews.llvm.org/D30771 llvm-svn: 301384
This commit is contained in:
parent
a5d4537077
commit
e3986c544d
|
@ -177,7 +177,10 @@ public:
|
|||
II_wcsdup(nullptr), II_win_wcsdup(nullptr), II_g_malloc(nullptr),
|
||||
II_g_malloc0(nullptr), II_g_realloc(nullptr), II_g_try_malloc(nullptr),
|
||||
II_g_try_malloc0(nullptr), II_g_try_realloc(nullptr),
|
||||
II_g_free(nullptr), II_g_memdup(nullptr) {}
|
||||
II_g_free(nullptr), II_g_memdup(nullptr), II_g_malloc_n(nullptr),
|
||||
II_g_malloc0_n(nullptr), II_g_realloc_n(nullptr),
|
||||
II_g_try_malloc_n(nullptr), II_g_try_malloc0_n(nullptr),
|
||||
II_g_try_realloc_n(nullptr) {}
|
||||
|
||||
/// In pessimistic mode, the checker assumes that it does not know which
|
||||
/// functions might free the memory.
|
||||
|
@ -241,7 +244,10 @@ private:
|
|||
*II_if_nameindex, *II_if_freenameindex, *II_wcsdup,
|
||||
*II_win_wcsdup, *II_g_malloc, *II_g_malloc0,
|
||||
*II_g_realloc, *II_g_try_malloc, *II_g_try_malloc0,
|
||||
*II_g_try_realloc, *II_g_free, *II_g_memdup;
|
||||
*II_g_try_realloc, *II_g_free, *II_g_memdup,
|
||||
*II_g_malloc_n, *II_g_malloc0_n, *II_g_realloc_n,
|
||||
*II_g_try_malloc_n, *II_g_try_malloc0_n,
|
||||
*II_g_try_realloc_n;
|
||||
mutable Optional<uint64_t> KernelZeroFlagVal;
|
||||
|
||||
void initIdentifierInfo(ASTContext &C) const;
|
||||
|
@ -321,9 +327,12 @@ private:
|
|||
bool &ReleasedAllocated,
|
||||
bool ReturnsNullOnFailure = false) const;
|
||||
|
||||
ProgramStateRef ReallocMem(CheckerContext &C, const CallExpr *CE,
|
||||
bool FreesMemOnFailure,
|
||||
ProgramStateRef State) const;
|
||||
ProgramStateRef ReallocMemAux(CheckerContext &C, const CallExpr *CE,
|
||||
bool FreesMemOnFailure,
|
||||
ProgramStateRef State,
|
||||
bool SuffixWithN = false) const;
|
||||
static SVal evalMulForBufferSize(CheckerContext &C, const Expr *Blocks,
|
||||
const Expr *BlockBytes);
|
||||
static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE,
|
||||
ProgramStateRef State);
|
||||
|
||||
|
@ -569,6 +578,12 @@ void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const {
|
|||
II_g_try_realloc = &Ctx.Idents.get("g_try_realloc");
|
||||
II_g_free = &Ctx.Idents.get("g_free");
|
||||
II_g_memdup = &Ctx.Idents.get("g_memdup");
|
||||
II_g_malloc_n = &Ctx.Idents.get("g_malloc_n");
|
||||
II_g_malloc0_n = &Ctx.Idents.get("g_malloc0_n");
|
||||
II_g_realloc_n = &Ctx.Idents.get("g_realloc_n");
|
||||
II_g_try_malloc_n = &Ctx.Idents.get("g_try_malloc_n");
|
||||
II_g_try_malloc0_n = &Ctx.Idents.get("g_try_malloc0_n");
|
||||
II_g_try_realloc_n = &Ctx.Idents.get("g_try_realloc_n");
|
||||
}
|
||||
|
||||
bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const {
|
||||
|
@ -617,7 +632,10 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD,
|
|||
FunI == II_g_malloc || FunI == II_g_malloc0 ||
|
||||
FunI == II_g_realloc || FunI == II_g_try_malloc ||
|
||||
FunI == II_g_try_malloc0 || FunI == II_g_try_realloc ||
|
||||
FunI == II_g_memdup)
|
||||
FunI == II_g_memdup || FunI == II_g_malloc_n ||
|
||||
FunI == II_g_malloc0_n || FunI == II_g_realloc_n ||
|
||||
FunI == II_g_try_malloc_n || FunI == II_g_try_malloc0_n ||
|
||||
FunI == II_g_try_realloc_n)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -767,6 +785,17 @@ llvm::Optional<ProgramStateRef> MallocChecker::performKernelMalloc(
|
|||
return None;
|
||||
}
|
||||
|
||||
SVal MallocChecker::evalMulForBufferSize(CheckerContext &C, const Expr *Blocks,
|
||||
const Expr *BlockBytes) {
|
||||
SValBuilder &SB = C.getSValBuilder();
|
||||
SVal BlocksVal = C.getSVal(Blocks);
|
||||
SVal BlockBytesVal = C.getSVal(BlockBytes);
|
||||
ProgramStateRef State = C.getState();
|
||||
SVal TotalSize = SB.evalBinOp(State, BO_Mul, BlocksVal, BlockBytesVal,
|
||||
SB.getContext().getSizeType());
|
||||
return TotalSize;
|
||||
}
|
||||
|
||||
void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
|
||||
if (C.wasInlined)
|
||||
return;
|
||||
|
@ -813,10 +842,10 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
|
|||
State = ProcessZeroAllocation(C, CE, 0, State);
|
||||
} else if (FunI == II_realloc || FunI == II_g_realloc ||
|
||||
FunI == II_g_try_realloc) {
|
||||
State = ReallocMem(C, CE, false, State);
|
||||
State = ReallocMemAux(C, CE, false, State);
|
||||
State = ProcessZeroAllocation(C, CE, 1, State);
|
||||
} else if (FunI == II_reallocf) {
|
||||
State = ReallocMem(C, CE, true, State);
|
||||
State = ReallocMemAux(C, CE, true, State);
|
||||
State = ProcessZeroAllocation(C, CE, 1, State);
|
||||
} else if (FunI == II_calloc) {
|
||||
State = CallocMem(C, CE, State);
|
||||
|
@ -874,6 +903,25 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
|
|||
return;
|
||||
State = MallocMemAux(C, CE, CE->getArg(1), UndefinedVal(), State);
|
||||
State = ProcessZeroAllocation(C, CE, 1, State);
|
||||
} else if (FunI == II_g_malloc_n || FunI == II_g_try_malloc_n ||
|
||||
FunI == II_g_malloc0_n || FunI == II_g_try_malloc0_n) {
|
||||
if (CE->getNumArgs() < 2)
|
||||
return;
|
||||
SVal Init = UndefinedVal();
|
||||
if (FunI == II_g_malloc0_n || FunI == II_g_try_malloc0_n) {
|
||||
SValBuilder &SB = C.getSValBuilder();
|
||||
Init = SB.makeZeroVal(SB.getContext().CharTy);
|
||||
}
|
||||
SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1));
|
||||
State = MallocMemAux(C, CE, TotalSize, Init, State);
|
||||
State = ProcessZeroAllocation(C, CE, 0, State);
|
||||
State = ProcessZeroAllocation(C, CE, 1, State);
|
||||
} else if (FunI == II_g_realloc_n || FunI == II_g_try_realloc_n) {
|
||||
if (CE->getNumArgs() < 3)
|
||||
return;
|
||||
State = ReallocMemAux(C, CE, false, State, true);
|
||||
State = ProcessZeroAllocation(C, CE, 1, State);
|
||||
State = ProcessZeroAllocation(C, CE, 2, State);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1976,14 +2024,17 @@ void MallocChecker::ReportUseZeroAllocated(CheckerContext &C,
|
|||
}
|
||||
}
|
||||
|
||||
ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
|
||||
const CallExpr *CE,
|
||||
bool FreesOnFail,
|
||||
ProgramStateRef State) const {
|
||||
ProgramStateRef MallocChecker::ReallocMemAux(CheckerContext &C,
|
||||
const CallExpr *CE,
|
||||
bool FreesOnFail,
|
||||
ProgramStateRef State,
|
||||
bool SuffixWithN) const {
|
||||
if (!State)
|
||||
return nullptr;
|
||||
|
||||
if (CE->getNumArgs() < 2)
|
||||
if (SuffixWithN && CE->getNumArgs() < 3)
|
||||
return nullptr;
|
||||
else if (CE->getNumArgs() < 2)
|
||||
return nullptr;
|
||||
|
||||
const Expr *arg0Expr = CE->getArg(0);
|
||||
|
@ -1998,20 +2049,19 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
|
|||
DefinedOrUnknownSVal PtrEQ =
|
||||
svalBuilder.evalEQ(State, arg0Val, svalBuilder.makeNull());
|
||||
|
||||
// Get the size argument. If there is no size arg then give up.
|
||||
// Get the size argument.
|
||||
const Expr *Arg1 = CE->getArg(1);
|
||||
if (!Arg1)
|
||||
return nullptr;
|
||||
|
||||
// Get the value of the size argument.
|
||||
SVal Arg1ValG = State->getSVal(Arg1, LCtx);
|
||||
if (!Arg1ValG.getAs<DefinedOrUnknownSVal>())
|
||||
SVal TotalSize = State->getSVal(Arg1, LCtx);
|
||||
if (SuffixWithN)
|
||||
TotalSize = evalMulForBufferSize(C, Arg1, CE->getArg(2));
|
||||
if (!TotalSize.getAs<DefinedOrUnknownSVal>())
|
||||
return nullptr;
|
||||
DefinedOrUnknownSVal Arg1Val = Arg1ValG.castAs<DefinedOrUnknownSVal>();
|
||||
|
||||
// Compare the size argument to 0.
|
||||
DefinedOrUnknownSVal SizeZero =
|
||||
svalBuilder.evalEQ(State, Arg1Val,
|
||||
svalBuilder.evalEQ(State, TotalSize.castAs<DefinedOrUnknownSVal>(),
|
||||
svalBuilder.makeIntValWithPtrWidth(0, false));
|
||||
|
||||
ProgramStateRef StatePtrIsNull, StatePtrNotNull;
|
||||
|
@ -2025,8 +2075,8 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
|
|||
|
||||
// If the ptr is NULL and the size is not 0, the call is equivalent to
|
||||
// malloc(size).
|
||||
if ( PrtIsNull && !SizeIsZero) {
|
||||
ProgramStateRef stateMalloc = MallocMemAux(C, CE, CE->getArg(1),
|
||||
if (PrtIsNull && !SizeIsZero) {
|
||||
ProgramStateRef stateMalloc = MallocMemAux(C, CE, TotalSize,
|
||||
UndefinedVal(), StatePtrIsNull);
|
||||
return stateMalloc;
|
||||
}
|
||||
|
@ -2059,7 +2109,7 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
|
|||
if (ProgramStateRef stateFree =
|
||||
FreeMemAux(C, CE, State, 0, false, ReleasedAllocated)) {
|
||||
|
||||
ProgramStateRef stateRealloc = MallocMemAux(C, CE, CE->getArg(1),
|
||||
ProgramStateRef stateRealloc = MallocMemAux(C, CE, TotalSize,
|
||||
UnknownVal(), stateFree);
|
||||
if (!stateRealloc)
|
||||
return nullptr;
|
||||
|
@ -2090,12 +2140,8 @@ ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE,
|
|||
return nullptr;
|
||||
|
||||
SValBuilder &svalBuilder = C.getSValBuilder();
|
||||
const LocationContext *LCtx = C.getLocationContext();
|
||||
SVal count = State->getSVal(CE->getArg(0), LCtx);
|
||||
SVal elementSize = State->getSVal(CE->getArg(1), LCtx);
|
||||
SVal TotalSize = svalBuilder.evalBinOp(State, BO_Mul, count, elementSize,
|
||||
svalBuilder.getContext().getSizeType());
|
||||
SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy);
|
||||
SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1));
|
||||
|
||||
return MallocMemAux(C, CE, TotalSize, zeroVal, State);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,12 @@ gpointer g_realloc(gpointer mem, gsize n_bytes);
|
|||
gpointer g_try_malloc(gsize n_bytes);
|
||||
gpointer g_try_malloc0(gsize n_bytes);
|
||||
gpointer g_try_realloc(gpointer mem, gsize n_bytes);
|
||||
gpointer g_malloc_n(gsize n_blocks, gsize n_block_bytes);
|
||||
gpointer g_malloc0_n(gsize n_blocks, gsize n_block_bytes);
|
||||
gpointer g_realloc_n(gpointer mem, gsize n_blocks, gsize n_block_bytes);
|
||||
gpointer g_try_malloc_n(gsize n_blocks, gsize n_block_bytes);
|
||||
gpointer g_try_malloc0_n(gsize n_blocks, gsize n_block_bytes);
|
||||
gpointer g_try_realloc_n(gpointer mem, gsize n_blocks, gsize n_block_bytes);
|
||||
void g_free(gpointer mem);
|
||||
gpointer g_memdup(gconstpointer mem, guint byte_size);
|
||||
|
||||
|
@ -25,6 +31,12 @@ void f1() {
|
|||
gpointer g3 = g_try_malloc(n_bytes);
|
||||
gpointer g4 = g_try_malloc0(n_bytes);
|
||||
g3 = g_try_realloc(g3, n_bytes * 2);
|
||||
gpointer g5 = g_malloc_n(n_bytes, sizeof(char));
|
||||
gpointer g6 = g_malloc0_n(n_bytes, sizeof(char));
|
||||
g5 = g_realloc_n(g5, n_bytes * 2, sizeof(char));
|
||||
gpointer g7 = g_try_malloc_n(n_bytes, sizeof(char));
|
||||
gpointer g8 = g_try_malloc0_n(n_bytes, sizeof(char));
|
||||
g7 = g_try_realloc_n(g7, n_bytes * 2, sizeof(char));
|
||||
|
||||
g_free(g1);
|
||||
g_free(g2);
|
||||
|
@ -38,6 +50,12 @@ void f2() {
|
|||
gpointer g3 = g_try_malloc(n_bytes);
|
||||
gpointer g4 = g_try_malloc0(n_bytes);
|
||||
g3 = g_try_realloc(g3, n_bytes * 2);
|
||||
gpointer g5 = g_malloc_n(n_bytes, sizeof(char));
|
||||
gpointer g6 = g_malloc0_n(n_bytes, sizeof(char));
|
||||
g5 = g_realloc_n(g5, n_bytes * 2, sizeof(char));
|
||||
gpointer g7 = g_try_malloc_n(n_bytes, sizeof(char));
|
||||
gpointer g8 = g_try_malloc0_n(n_bytes, sizeof(char));
|
||||
g7 = g_try_realloc_n(g7, n_bytes * 2, sizeof(char));
|
||||
|
||||
g_free(g1);
|
||||
g_free(g2);
|
||||
|
@ -52,8 +70,100 @@ void f3() {
|
|||
gpointer g3 = g_try_malloc(n_bytes);
|
||||
gpointer g4 = g_try_malloc0(n_bytes);
|
||||
g3 = g_try_realloc(g3, n_bytes * 2); // expected-warning{{Potential leak of memory pointed to by 'g4'}}
|
||||
gpointer g5 = g_malloc_n(n_bytes, sizeof(char));
|
||||
gpointer g6 = g_malloc0_n(n_bytes, sizeof(char));
|
||||
g5 = g_realloc_n(g5, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g6'}}
|
||||
gpointer g7 = g_try_malloc_n(n_bytes, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g5'}}
|
||||
gpointer g8 = g_try_malloc0_n(n_bytes, sizeof(char));
|
||||
g7 = g_try_realloc_n(g7, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g8'}}
|
||||
|
||||
g_free(g1); // expected-warning{{Potential leak of memory pointed to by 'g7'}}
|
||||
g_free(g2);
|
||||
g_free(g3);
|
||||
}
|
||||
|
||||
void f4() {
|
||||
gpointer g1 = g_malloc(n_bytes);
|
||||
gpointer g2 = g_malloc0(n_bytes);
|
||||
g1 = g_realloc(g1, n_bytes * 2);
|
||||
gpointer g3 = g_try_malloc(n_bytes);
|
||||
gpointer g4 = g_try_malloc0(n_bytes);
|
||||
g3 = g_try_realloc(g3, n_bytes * 2);
|
||||
gpointer g5 = g_malloc_n(n_bytes, sizeof(char));
|
||||
gpointer g6 = g_malloc0_n(n_bytes, sizeof(char));
|
||||
g5 = g_realloc_n(g5, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g6'}}
|
||||
gpointer g7 = g_try_malloc_n(n_bytes, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g5'}}
|
||||
gpointer g8 = g_try_malloc0_n(n_bytes, sizeof(char));
|
||||
g7 = g_try_realloc_n(g7, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g8'}}
|
||||
|
||||
g_free(g1); // expected-warning{{Potential leak of memory pointed to by 'g7'}}
|
||||
g_free(g2);
|
||||
g_free(g3);
|
||||
g_free(g4);
|
||||
}
|
||||
|
||||
void f5() {
|
||||
gpointer g1 = g_malloc(n_bytes);
|
||||
gpointer g2 = g_malloc0(n_bytes);
|
||||
g1 = g_realloc(g1, n_bytes * 2);
|
||||
gpointer g3 = g_try_malloc(n_bytes);
|
||||
gpointer g4 = g_try_malloc0(n_bytes);
|
||||
g3 = g_try_realloc(g3, n_bytes * 2);
|
||||
gpointer g5 = g_malloc_n(n_bytes, sizeof(char));
|
||||
gpointer g6 = g_malloc0_n(n_bytes, sizeof(char));
|
||||
g5 = g_realloc_n(g5, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g6'}}
|
||||
gpointer g7 = g_try_malloc_n(n_bytes, sizeof(char));
|
||||
gpointer g8 = g_try_malloc0_n(n_bytes, sizeof(char));
|
||||
g7 = g_try_realloc_n(g7, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g8'}}
|
||||
|
||||
g_free(g1); // expected-warning{{Potential leak of memory pointed to by 'g7'}}
|
||||
g_free(g2);
|
||||
g_free(g3);
|
||||
g_free(g4);
|
||||
g_free(g5);
|
||||
}
|
||||
|
||||
void f6() {
|
||||
gpointer g1 = g_malloc(n_bytes);
|
||||
gpointer g2 = g_malloc0(n_bytes);
|
||||
g1 = g_realloc(g1, n_bytes * 2);
|
||||
gpointer g3 = g_try_malloc(n_bytes);
|
||||
gpointer g4 = g_try_malloc0(n_bytes);
|
||||
g3 = g_try_realloc(g3, n_bytes * 2);
|
||||
gpointer g5 = g_malloc_n(n_bytes, sizeof(char));
|
||||
gpointer g6 = g_malloc0_n(n_bytes, sizeof(char));
|
||||
g5 = g_realloc_n(g5, n_bytes * 2, sizeof(char));
|
||||
gpointer g7 = g_try_malloc_n(n_bytes, sizeof(char));
|
||||
gpointer g8 = g_try_malloc0_n(n_bytes, sizeof(char));
|
||||
g7 = g_try_realloc_n(g7, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g8'}}
|
||||
|
||||
g_free(g1); // expected-warning{{Potential leak of memory pointed to by 'g7'}}
|
||||
g_free(g2);
|
||||
g_free(g3);
|
||||
g_free(g4);
|
||||
g_free(g5);
|
||||
g_free(g6);
|
||||
}
|
||||
|
||||
void f7() {
|
||||
gpointer g1 = g_malloc(n_bytes);
|
||||
gpointer g2 = g_malloc0(n_bytes);
|
||||
g1 = g_realloc(g1, n_bytes * 2);
|
||||
gpointer g3 = g_try_malloc(n_bytes);
|
||||
gpointer g4 = g_try_malloc0(n_bytes);
|
||||
g3 = g_try_realloc(g3, n_bytes * 2);
|
||||
gpointer g5 = g_malloc_n(n_bytes, sizeof(char));
|
||||
gpointer g6 = g_malloc0_n(n_bytes, sizeof(char));
|
||||
g5 = g_realloc_n(g5, n_bytes * 2, sizeof(char));
|
||||
gpointer g7 = g_try_malloc_n(n_bytes, sizeof(char));
|
||||
gpointer g8 = g_try_malloc0_n(n_bytes, sizeof(char));
|
||||
g7 = g_try_realloc_n(g7, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g8'}}
|
||||
|
||||
g_free(g1);
|
||||
g_free(g2);
|
||||
g_free(g3);
|
||||
g_free(g4);
|
||||
g_free(g5);
|
||||
g_free(g6);
|
||||
g_free(g7);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue