diff --git a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp --- a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp @@ -51,11 +51,14 @@ ArrayRef ExplicitRegions, ArrayRef Regions, const LocationContext *LCtx, const CallEvent *Call) const; + void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, + const char *Sep) const; private: void handleReset(const CallEvent &Call, CheckerContext &C) const; void handleRelease(const CallEvent &Call, CheckerContext &C) const; void handleSwap(const CallEvent &Call, CheckerContext &C) const; + void handleBoolOperation(const CallEvent &Call, CheckerContext &C) const; using SmartPtrMethodHandlerFn = void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const; @@ -141,17 +144,23 @@ const MemRegion *ThisR = cast(&Call)->getCXXThisVal().getAsRegion(); - if (!move::isMovedFrom(State, ThisR)) { - // TODO: Model this case as well. At least, avoid invalidation of - // globals. - return false; + if (ModelSmartPtrDereference) { + handleBoolOperation(Call, C); + return true; + } else { + if (!move::isMovedFrom(State, ThisR)) { + // TODO: Model this case as well. At least, avoid invalidation of + // globals. + return false; + } + + // TODO: Add a note to bug reports describing this decision. + C.addTransition(State->BindExpr( + Call.getOriginExpr(), C.getLocationContext(), + C.getSValBuilder().makeZeroVal(Call.getResultType()))); + + return true; } - - // TODO: Add a note to bug reports describing this decision. - C.addTransition( - State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), - C.getSValBuilder().makeZeroVal(Call.getResultType()))); - return true; } if (!ModelSmartPtrDereference) @@ -227,6 +236,23 @@ C.addTransition(State); } +void SmartPtrModeling::printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const { + TrackedRegionMapTy RS = State->get(); + + if (!RS.isEmpty()) { + Out << Sep << "Smart ptr regions :" << NL; + for (auto I : RS) { + I.first->dumpToStream(Out); + if (smartptr::isNullSmartPtr(State, I.first)) + Out << ": Null"; + else + Out << ": Non Null"; + Out << NL; + } + } +} + ProgramStateRef SmartPtrModeling::checkRegionChanges( ProgramStateRef State, const InvalidatedSymbols *Invalidated, ArrayRef ExplicitRegions, @@ -345,6 +371,55 @@ })); } +void SmartPtrModeling::handleBoolOperation(const CallEvent &Call, + CheckerContext &C) const { + // To model unique_ptr::operator bool + ProgramStateRef State = C.getState(); + const MemRegion *ThisRegion = + cast(&Call)->getCXXThisVal().getAsRegion(); + const auto *InnerPointVal = State->get(ThisRegion); + if (InnerPointVal) { + if (InnerPointVal->isZeroConstant()) { + State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), + C.getSValBuilder().makeTruthVal(false)); + } else { + State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), + C.getSValBuilder().makeTruthVal(true)); + } + C.addTransition(State); + } else if (move::isMovedFrom(State, ThisRegion)) { + C.addTransition( + State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), + C.getSValBuilder().makeZeroVal(Call.getResultType()))); + } else { + const Expr *CallExpr = Call.getOriginExpr(); + auto CallExprVal = State->getSVal(CallExpr, Call.getLocationContext()) + .getAs(); + if (CallExprVal) { + ProgramStateRef NotNullState, NullState; + std::tie(NotNullState, NullState) = State->assume(CallExprVal.getValue()); + + if (NullState) { + auto NullVal = C.getSValBuilder().makeNull(); + NullState = NullState->set(ThisRegion, NullVal); + NullState = + NullState->BindExpr(Call.getOriginExpr(), C.getLocationContext(), + C.getSValBuilder().makeTruthVal(false)); + C.addTransition(NullState); + } + if (NotNullState) { + auto NonNullVal = C.getSValBuilder().makeTruthVal(true); + NotNullState = + NotNullState->set(ThisRegion, NonNullVal); + NotNullState = + NotNullState->BindExpr(Call.getOriginExpr(), C.getLocationContext(), + C.getSValBuilder().makeTruthVal(true)); + C.addTransition(NotNullState); + } + } + } +} + void ento::registerSmartPtrModeling(CheckerManager &Mgr) { auto *Checker = Mgr.registerChecker(); Checker->ModelSmartPtrDereference = diff --git a/clang/test/Analysis/smart-ptr.cpp b/clang/test/Analysis/smart-ptr.cpp --- a/clang/test/Analysis/smart-ptr.cpp +++ b/clang/test/Analysis/smart-ptr.cpp @@ -87,6 +87,7 @@ void derefAfterResetWithNonNull() { std::unique_ptr P; P.reset(new A()); + clang_analyzer_numTimesReached(); // expected-warning {{1}} P->foo(); // No warning. } @@ -117,36 +118,39 @@ void pass_smart_ptr_by_const_ptr(const std::unique_ptr *a); void regioninvalidationTest() { - { - std::unique_ptr P; - pass_smart_ptr_by_ref(P); - P->foo(); // no-warning - } - { - std::unique_ptr P; - pass_smart_ptr_by_const_ref(P); - P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} - } - { - std::unique_ptr P; - pass_smart_ptr_by_rvalue_ref(std::move(P)); - P->foo(); // no-warning - } - { - std::unique_ptr P; - pass_smart_ptr_by_const_rvalue_ref(std::move(P)); - P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} - } - { - std::unique_ptr P; - pass_smart_ptr_by_ptr(&P); - P->foo(); - } - { - std::unique_ptr P; - pass_smart_ptr_by_const_ptr(&P); - P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} - } + std::unique_ptr P; + pass_smart_ptr_by_ref(P); + P->foo(); // no-warning +} + +void regioninvalidationTest1() { + std::unique_ptr P; + pass_smart_ptr_by_const_ref(P); + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} +} + +void regioninvalidationTest2() { + std::unique_ptr P; + pass_smart_ptr_by_rvalue_ref(std::move(P)); + P->foo(); // no-warning +} + +void regioninvalidationTest3() { + std::unique_ptr P; + pass_smart_ptr_by_const_rvalue_ref(std::move(P)); + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} +} + +void regioninvalidationTest4() { + std::unique_ptr P; + pass_smart_ptr_by_ptr(&P); + P->foo(); +} + +void regioninvalidationTest5() { + std::unique_ptr P; + pass_smart_ptr_by_const_ptr(&P); + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} } struct StructWithSmartPtr { @@ -161,36 +165,39 @@ void pass_struct_with_smart_ptr_by_const_ptr(const StructWithSmartPtr *a); void regioninvalidationTestWithinStruct() { - { - StructWithSmartPtr S; - pass_struct_with_smart_ptr_by_ref(S); - S.P->foo(); // no-warning - } - { - StructWithSmartPtr S; - pass_struct_with_smart_ptr_by_const_ref(S); - S.P->foo(); // expected-warning {{Dereference of null smart pointer 'S.P' [alpha.cplusplus.SmartPtr]}} - } - { - StructWithSmartPtr S; - pass_struct_with_smart_ptr_by_rvalue_ref(std::move(S)); - S.P->foo(); // no-warning - } - { - StructWithSmartPtr S; - pass_struct_with_smart_ptr_by_const_rvalue_ref(std::move(S)); - S.P->foo(); // expected-warning {{Dereference of null smart pointer 'S.P' [alpha.cplusplus.SmartPtr]}} - } - { - StructWithSmartPtr S; - pass_struct_with_smart_ptr_by_ptr(&S); - S.P->foo(); - } - { - StructWithSmartPtr S; - pass_struct_with_smart_ptr_by_const_ptr(&S); - S.P->foo(); // expected-warning {{Dereference of null smart pointer 'S.P' [alpha.cplusplus.SmartPtr]}} - } + StructWithSmartPtr S; + pass_struct_with_smart_ptr_by_ref(S); + S.P->foo(); // no-warning +} + +void regioninvalidationTestWithinStruct2() { + StructWithSmartPtr S; + pass_struct_with_smart_ptr_by_const_ref(S); + S.P->foo(); // expected-warning {{Dereference of null smart pointer 'S.P' [alpha.cplusplus.SmartPtr]}} +} + +void regioninvalidationTestWithinStruct3() { + StructWithSmartPtr S; + pass_struct_with_smart_ptr_by_rvalue_ref(std::move(S)); + S.P->foo(); // no-warning +} + +void regioninvalidationTestWithinStruct4() { + StructWithSmartPtr S; + pass_struct_with_smart_ptr_by_const_rvalue_ref(std::move(S)); + S.P->foo(); // expected-warning {{Dereference of null smart pointer 'S.P' [alpha.cplusplus.SmartPtr]}} +} + +void regioninvalidationTestWithinStruct5() { + StructWithSmartPtr S; + pass_struct_with_smart_ptr_by_ptr(&S); + S.P->foo(); // no-warning +} + +void regioninvalidationTestWithinStruct6() { + StructWithSmartPtr S; + pass_struct_with_smart_ptr_by_const_ptr(&S); + S.P->foo(); // expected-warning {{Dereference of null smart pointer 'S.P' [alpha.cplusplus.SmartPtr]}} } void derefAfterAssignment() { @@ -217,14 +224,20 @@ (*P).foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} } -void derefOnStdSwappedNullPtr() { +void derefOnFirstStdSwappedNullPtr() { std::unique_ptr P; std::unique_ptr PNull; std::swap(P, PNull); - PNull->foo(); // expected-warning {{Dereference of null smart pointer 'PNull' [alpha.cplusplus.SmartPtr]}} P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} } +void derefOnSecondStdSwappedNullPtr() { + std::unique_ptr P; + std::unique_ptr PNull; + std::swap(P, PNull); + PNull->foo(); // expected-warning {{Dereference of null smart pointer 'PNull' [alpha.cplusplus.SmartPtr]}} +} + void derefOnSwappedValidPtr() { std::unique_ptr P(new A()); std::unique_ptr PValid(new A()); @@ -235,3 +248,45 @@ P->foo(); // No warning. PValid->foo(); // No warning. } + +void derefConditionOnNullPtr() { + std::unique_ptr P; + if (P) + P->foo(); // No warning. +} + +void derefConditionOnNotNullPtr() { + std::unique_ptr P; + if (!P) + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} +} + +void derefConditionOnValidPtr() { + std::unique_ptr P(new A()); + std::unique_ptr PNull; + if (P) + PNull->foo(); // expected-warning {{Dereference of null smart pointer 'PNull' [alpha.cplusplus.SmartPtr]}} +} + +void derefConditionOnNotValidPtr() { + std::unique_ptr P(new A()); + std::unique_ptr PNull; + if (!P) + PNull->foo(); // expected-warning {{Dereference of null smart pointer 'PNull' [alpha.cplusplus.SmartPtr]}} + // FIXME: This should go away once we fix the splitting bug with ctr evalCall +} + +void derefConditionOnUnKnownPtr(std::unique_ptr P) { + if (P) + P->foo(); // No warning. + else + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} +} + +void derefOnValidPtrAfterReset(std::unique_ptr P) { + P.reset(new A()); + if (!P) + P->foo(); // No warning. + else + P->foo(); // No warning. +}