Skip to content
This repository was archived by the owner on Apr 23, 2020. It is now read-only.

Commit 55dd956

Browse files
committed
[analyzer] Fix realloc related bug in the malloc checker.
When reallocation of a non-allocated (not owned) symbol fails do not expect it to be freed. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@162533 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 4f534e7 commit 55dd956

File tree

2 files changed

+52
-17
lines changed

2 files changed

+52
-17
lines changed

lib/StaticAnalyzer/Checkers/MallocChecker.cpp

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,16 @@ class RefState {
7070
}
7171
};
7272

73+
/// \class ReallocPair
74+
/// \brief Stores information about the symbol being reallocated by a call to
75+
/// 'realloc' to allow modeling failed reallocation later in the path.
7376
struct ReallocPair {
77+
// \brief The symbol which realloc reallocated.
7478
SymbolRef ReallocatedSym;
79+
// \brief The flag is true if the symbol does not need to be freed after
80+
// reallocation fails.
7581
bool IsFreeOnFailure;
82+
7683
ReallocPair(SymbolRef S, bool F) : ReallocatedSym(S), IsFreeOnFailure(F) {}
7784
void Profile(llvm::FoldingSetNodeID &ID) const {
7885
ID.AddInteger(IsFreeOnFailure);
@@ -177,11 +184,13 @@ class MallocChecker : public Checker<check::DeadSymbols,
177184
const OwnershipAttr* Att) const;
178185
ProgramStateRef FreeMemAux(CheckerContext &C, const CallExpr *CE,
179186
ProgramStateRef state, unsigned Num,
180-
bool Hold) const;
187+
bool Hold,
188+
bool &ReleasedAllocated) const;
181189
ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *Arg,
182190
const Expr *ParentExpr,
183191
ProgramStateRef state,
184-
bool Hold) const;
192+
bool Hold,
193+
bool &ReleasedAllocated) const;
185194

186195
ProgramStateRef ReallocMem(CheckerContext &C, const CallExpr *CE,
187196
bool FreesMemOnFailure) const;
@@ -431,6 +440,7 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
431440
return;
432441

433442
ProgramStateRef State = C.getState();
443+
bool ReleasedAllocatedMemory = false;
434444

435445
if (FD->getKind() == Decl::Function) {
436446
initIdentifierInfo(C.getASTContext());
@@ -447,7 +457,7 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
447457
} else if (FunI == II_calloc) {
448458
State = CallocMem(C, CE);
449459
} else if (FunI == II_free) {
450-
State = FreeMemAux(C, CE, State, 0, false);
460+
State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);
451461
} else if (FunI == II_strdup) {
452462
State = MallocUpdateRefState(C, CE, State);
453463
} else if (FunI == II_strndup) {
@@ -494,14 +504,16 @@ void MallocChecker::checkPreObjCMessage(const ObjCMethodCall &Call,
494504
// Ex: [NSData dataWithBytesNoCopy:bytes length:10];
495505
// Unless 'freeWhenDone' param set to 0.
496506
// TODO: Check that the memory was allocated with malloc.
507+
bool ReleasedAllocatedMemory = false;
497508
Selector S = Call.getSelector();
498509
if ((S.getNameForSlot(0) == "dataWithBytesNoCopy" ||
499510
S.getNameForSlot(0) == "initWithBytesNoCopy" ||
500511
S.getNameForSlot(0) == "initWithCharactersNoCopy") &&
501512
!isFreeWhenDoneSetToZero(Call)){
502513
unsigned int argIdx = 0;
503514
C.addTransition(FreeMemAux(C, Call.getArgExpr(argIdx),
504-
Call.getOriginExpr(), C.getState(), true));
515+
Call.getOriginExpr(), C.getState(), true,
516+
ReleasedAllocatedMemory));
505517
}
506518
}
507519

@@ -584,11 +596,13 @@ ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
584596
return 0;
585597

586598
ProgramStateRef State = C.getState();
599+
bool ReleasedAllocated = false;
587600

588601
for (OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end();
589602
I != E; ++I) {
590603
ProgramStateRef StateI = FreeMemAux(C, CE, State, *I,
591-
Att->getOwnKind() == OwnershipAttr::Holds);
604+
Att->getOwnKind() == OwnershipAttr::Holds,
605+
ReleasedAllocated);
592606
if (StateI)
593607
State = StateI;
594608
}
@@ -599,18 +613,20 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
599613
const CallExpr *CE,
600614
ProgramStateRef state,
601615
unsigned Num,
602-
bool Hold) const {
616+
bool Hold,
617+
bool &ReleasedAllocated) const {
603618
if (CE->getNumArgs() < (Num + 1))
604619
return 0;
605620

606-
return FreeMemAux(C, CE->getArg(Num), CE, state, Hold);
621+
return FreeMemAux(C, CE->getArg(Num), CE, state, Hold, ReleasedAllocated);
607622
}
608623

609624
ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
610625
const Expr *ArgExpr,
611626
const Expr *ParentExpr,
612627
ProgramStateRef state,
613-
bool Hold) const {
628+
bool Hold,
629+
bool &ReleasedAllocated) const {
614630

615631
SVal ArgVal = state->getSVal(ArgExpr, C.getLocationContext());
616632
if (!isa<DefinedOrUnknownSVal>(ArgVal))
@@ -691,6 +707,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
691707
return 0;
692708
}
693709

710+
ReleasedAllocated = (RS != 0);
711+
694712
// Normal free.
695713
if (Hold)
696714
return state->set<RegionState>(Sym, RefState::getRelinquished(ParentExpr));
@@ -886,9 +904,12 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
886904
if (!FromPtr || !ToPtr)
887905
return 0;
888906

907+
bool ReleasedAllocated = false;
908+
889909
// If the size is 0, free the memory.
890910
if (SizeIsZero)
891-
if (ProgramStateRef stateFree = FreeMemAux(C, CE, StateSizeIsZero,0,false)){
911+
if (ProgramStateRef stateFree = FreeMemAux(C, CE, StateSizeIsZero, 0,
912+
false, ReleasedAllocated)){
892913
// The semantics of the return value are:
893914
// If size was equal to 0, either NULL or a pointer suitable to be passed
894915
// to free() is returned. We just free the input pointer and do not add
@@ -897,14 +918,19 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
897918
}
898919

899920
// Default behavior.
900-
if (ProgramStateRef stateFree = FreeMemAux(C, CE, state, 0, false)) {
901-
// FIXME: We should copy the content of the original buffer.
921+
if (ProgramStateRef stateFree =
922+
FreeMemAux(C, CE, state, 0, false, ReleasedAllocated)) {
923+
902924
ProgramStateRef stateRealloc = MallocMemAux(C, CE, CE->getArg(1),
903925
UnknownVal(), stateFree);
904926
if (!stateRealloc)
905927
return 0;
928+
929+
// Record the info about the reallocated symbol so that we could properly
930+
// process failed reallocation.
906931
stateRealloc = stateRealloc->set<ReallocPairs>(ToPtr,
907-
ReallocPair(FromPtr, FreesOnFail));
932+
ReallocPair(FromPtr, FreesOnFail || !ReleasedAllocated));
933+
// The reallocated symbol should stay alive for as long as the new symbol.
908934
C.getSymbolManager().addSymbolDependency(ToPtr, FromPtr);
909935
return stateRealloc;
910936
}

test/Analysis/malloc.c

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,17 +1000,26 @@ void freeButNoMalloc(int *p, int x){
10001000
}
10011001

10021002
struct HasPtr {
1003-
int *p;
1003+
char *p;
10041004
};
10051005

1006-
int* reallocButNoMalloc(struct HasPtr *a, int c, int size) {
1006+
char* reallocButNoMalloc(struct HasPtr *a, int c, int size) {
10071007
int *s;
1008-
a->p = (int *)realloc(a->p, size);
1009-
if (a->p == 0)
1010-
return 0; // expected-warning{{Memory is never released; potential leak}}
1008+
char *b = realloc(a->p, size);
1009+
char *m = realloc(a->p, size); // expected-warning {{Attempt to free released memory}}
10111010
return a->p;
10121011
}
10131012

1013+
// We should not warn in this case since the caller will presumably free a->p in all cases.
1014+
int reallocButNoMallocPR13674(struct HasPtr *a, int c, int size) {
1015+
int *s;
1016+
char *b = realloc(a->p, size);
1017+
if (b == 0)
1018+
return -1;
1019+
a->p = b;
1020+
return 0;
1021+
}
1022+
10141023
// ----------------------------------------------------------------------------
10151024
// False negatives.
10161025

0 commit comments

Comments
 (0)