diff options
-rw-r--r-- | clang/include/clang/Analysis/CFG.h | 2 | ||||
-rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | 3 | ||||
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/CallEvent.cpp | 2 | ||||
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 25 | ||||
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 4 | ||||
-rw-r--r-- | clang/test/Analysis/NewDelete-checker-test.cpp | 25 | ||||
-rw-r--r-- | clang/test/Analysis/new.cpp | 145 |
7 files changed, 202 insertions, 4 deletions
diff --git a/clang/include/clang/Analysis/CFG.h b/clang/include/clang/Analysis/CFG.h index 2a601356001..14b7ab84267 100644 --- a/clang/include/clang/Analysis/CFG.h +++ b/clang/include/clang/Analysis/CFG.h @@ -200,7 +200,7 @@ public: } // Get Delete expression which triggered the destructor call. - const CXXDeleteExpr *getDeleteExpr() { + const CXXDeleteExpr *getDeleteExpr() const { return static_cast<CXXDeleteExpr *>(Data2.getPointer()); } diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 1fa15d09cbc..c7aa0fb150c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -1803,7 +1803,8 @@ bool MallocChecker::isReleased(SymbolRef Sym, CheckerContext &C) const { bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const { - if (isReleased(Sym, C)) { + // FIXME: Handle destructor called from delete more precisely. + if (isReleased(Sym, C) && S) { ReportUseAfterFree(C, S->getSourceRange(), Sym); return true; } diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp index 556f93b7cfb..ce66363413f 100644 --- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -964,6 +964,8 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx, const Stmt *Trigger; if (Optional<CFGAutomaticObjDtor> AutoDtor = E.getAs<CFGAutomaticObjDtor>()) Trigger = AutoDtor->getTriggerStmt(); + else if (Optional<CFGDeleteDtor> DeleteDtor = E.getAs<CFGDeleteDtor>()) + Trigger = cast<Stmt>(DeleteDtor->getDeleteExpr()); else Trigger = Dtor->getBody(); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 91acd55bc7f..411e2f6aea3 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -569,7 +569,30 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor, void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - //TODO: Handle DeleteDtor + ProgramStateRef State = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + const CXXDeleteExpr *DE = Dtor.getDeleteExpr(); + const Stmt *Arg = DE->getArgument(); + SVal ArgVal = State->getSVal(Arg, LCtx); + + // If the argument to delete is known to be a null value, + // don't run destructor. + if (State->isNull(ArgVal).isConstrainedTrue()) { + QualType DTy = DE->getDestroyedType(); + QualType BTy = getContext().getBaseElementType(DTy); + const CXXRecordDecl *RD = BTy->getAsCXXRecordDecl(); + const CXXDestructorDecl *Dtor = RD->getDestructor(); + + PostImplicitCall PP(Dtor, DE->getLocStart(), LCtx); + NodeBuilder Bldr(Pred, Dst, *currBldrCtx); + Bldr.generateNode(PP, Pred->getState(), Pred); + return; + } + + VisitCXXDestructor(DE->getDestroyedType(), + ArgVal.getAsRegion(), + DE, /*IsBase=*/ false, + Pred, Dst); } void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D, diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index b128bee1429..eba4f94d80e 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -296,7 +296,9 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, // FIXME: We need to run the same destructor on every element of the array. // This workaround will just run the first destructor (which will still // invalidate the entire array). - SVal DestVal = loc::MemRegionVal(Dest); + SVal DestVal = UnknownVal(); + if (Dest) + DestVal = loc::MemRegionVal(Dest); DestVal = makeZeroElementRegion(State, DestVal, ObjectType); Dest = DestVal.getAsRegion(); diff --git a/clang/test/Analysis/NewDelete-checker-test.cpp b/clang/test/Analysis/NewDelete-checker-test.cpp index 442b8fbef09..cc9725128bc 100644 --- a/clang/test/Analysis/NewDelete-checker-test.cpp +++ b/clang/test/Analysis/NewDelete-checker-test.cpp @@ -333,3 +333,28 @@ namespace reference_count { } } +// Test double delete +class DerefClass{ +public: + int *x; + DerefClass() {} + ~DerefClass() {*x = 1;} //expected-warning {{Use of memory after it is freed}} +}; + +void testDoubleDeleteClassInstance() { + DerefClass *foo = new DerefClass(); + delete foo; + delete foo; // FIXME: We should ideally report warning here instead of inside the destructor. +} + +class EmptyClass{ +public: + EmptyClass() {} + ~EmptyClass() {} +}; + +void testDoubleDeleteEmptyClass() { + EmptyClass *foo = new EmptyClass(); + delete foo; + delete foo; //expected-warning {{Attempt to free released memory}} +} diff --git a/clang/test/Analysis/new.cpp b/clang/test/Analysis/new.cpp index 27cbb0816b2..105a973ccc9 100644 --- a/clang/test/Analysis/new.cpp +++ b/clang/test/Analysis/new.cpp @@ -206,3 +206,148 @@ int testNoInitializationPlacement() { } return 1; } + +// Test modelling destructor call on call to delete +class IntPair{ +public: + int x; + int y; + IntPair() {}; + ~IntPair() {x = x/y;}; //expected-warning {{Division by zero}} +}; + +void testCallToDestructor() { + IntPair *b = new IntPair(); + b->x = 1; + b->y = 0; + delete b; // This results in divide by zero in destructor +} + +// Test Deleting a value that's passed as an argument. +class DerefClass{ +public: + int *x; + DerefClass() {}; + ~DerefClass() {*x = 1;}; //expected-warning {{Dereference of null pointer (loaded from field 'x')}} +}; + +void testDestCall(DerefClass *arg) { + delete arg; +} + +void test_delete_dtor_Arg() { + DerefClass *pair = new DerefClass(); + pair->x = 0; + testDestCall(pair); +} + +//Deleting the address of a local variable, null pointer +void abort(void) __attribute__((noreturn)); + +class NoReturnDtor { +public: + NoReturnDtor() {} + ~NoReturnDtor() {abort();} +}; + +void test_delete_dtor_LocalVar() { + NoReturnDtor test; + delete &test; // no warn or crash +} + +class DerivedNoReturn:public NoReturnDtor { +public: + DerivedNoReturn() {}; + ~DerivedNoReturn() {}; +}; + +void testNullDtorDerived() { + DerivedNoReturn *p = new DerivedNoReturn(); + delete p; // Calls the base destructor which aborts, checked below + clang_analyzer_eval(true); // no warn +} + +//Deleting a non class pointer should not crash/warn +void test_var_delete() { + int *v = new int; + delete v; // no crash/warn + clang_analyzer_eval(true); // expected-warning{{TRUE}} +} + +void testDeleteNull() { + NoReturnDtor *foo = 0; + delete foo; // should not call destructor, checked below + clang_analyzer_eval(true); // expected-warning{{TRUE}} +} + +void testNullAssigneddtor() { + NoReturnDtor *p = 0; + NoReturnDtor *s = p; + delete s; // should not call destructor, checked below + clang_analyzer_eval(true); // expected-warning{{TRUE}} +} + +void deleteArg(NoReturnDtor *test) { + delete test; +} + +void testNulldtorArg() { + NoReturnDtor *p = 0; + deleteArg(p); + clang_analyzer_eval(true); // expected-warning{{TRUE}} +} + +void testDeleteUnknown(NoReturnDtor *foo) { + delete foo; // should assume non-null and call noreturn destructor + clang_analyzer_eval(true); // no-warning +} + +void testArrayNull() { + NoReturnDtor *fooArray = 0; + delete[] fooArray; // should not call destructor, checked below + clang_analyzer_eval(true); // expected-warning{{TRUE}} +} + +void testArrayDestr() { + NoReturnDtor *p = new NoReturnDtor[2]; + delete[] p; // Calls the base destructor which aborts, checked below + //TODO: clang_analyzer_eval should not be called + clang_analyzer_eval(true); // expected-warning{{TRUE}} +} + +// Invalidate Region even in case of default destructor +class InvalidateDestTest { +public: + int x; + int *y; + ~InvalidateDestTest(); +}; + +int test_member_invalidation() { + + //test invalidation of member variable + InvalidateDestTest *test = new InvalidateDestTest(); + test->x = 5; + int *k = &(test->x); + clang_analyzer_eval(*k == 5); // expected-warning{{TRUE}} + delete test; + clang_analyzer_eval(*k == 5); // expected-warning{{UNKNOWN}} + + //test invalidation of member pointer + int localVar = 5; + test = new InvalidateDestTest(); + test->y = &localVar; + delete test; + clang_analyzer_eval(localVar == 5); // expected-warning{{UNKNOWN}} + + // Test aray elements are invalidated. + int Var1 = 5; + int Var2 = 5; + InvalidateDestTest *a = new InvalidateDestTest[2]; + a[0].y = &Var1; + a[1].y = &Var2; + delete[] a; + clang_analyzer_eval(Var1 == 5); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(Var2 == 5); // expected-warning{{UNKNOWN}} + return 0; +} |