RedDocMD updated this revision to Diff 352127. RedDocMD added a comment. Fixed up meaning of make_unique_for_overwrite, use `getConjuredHeapSymbolVal`.
Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D103750/new/ https://reviews.llvm.org/D103750 Files: clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp clang/lib/StaticAnalyzer/Core/SValBuilder.cpp clang/test/Analysis/Inputs/system-header-simulator-cxx.h clang/test/Analysis/smart-ptr-text-output.cpp
Index: clang/test/Analysis/smart-ptr-text-output.cpp =================================================================== --- clang/test/Analysis/smart-ptr-text-output.cpp +++ clang/test/Analysis/smart-ptr-text-output.cpp @@ -1,3 +1,8 @@ +// RUN: %clang_analyze_cc1\ +// RUN: -analyzer-checker=core,cplusplus.Move,alpha.cplusplus.SmartPtr\ +// RUN: -analyzer-config cplusplus.SmartPtrModeling:ModelSmartPtrDereference=true\ +// RUN: -analyzer-output=text -std=c++20 %s -verify=expected + // RUN: %clang_analyze_cc1\ // RUN: -analyzer-checker=core,cplusplus.Move,alpha.cplusplus.SmartPtr\ // RUN: -analyzer-config cplusplus.SmartPtrModeling:ModelSmartPtrDereference=true\ @@ -313,3 +318,35 @@ // expected-note@-1{{Dereference of null smart pointer 'P'}} } } + +void makeUniqueReturnsNonNullUniquePtr() { + auto P = std::make_unique<A>(); + if (!P) { // expected-note {{Taking false branch}} + P->foo(); // should have no warning here, path is impossible + } + P.reset(); // expected-note {{Smart pointer 'P' reset using a null value}} + // Now P is null + if (!P) { + // expected-note@-1 {{Taking true branch}} + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1{{Dereference of null smart pointer 'P'}} + } +} + +#if __cplusplus >= 202002L + +void makeUniqueForOverwriteReturnsNullUniquePtr() { + auto P = std::make_unique_for_overwrite<A>(); + if (!P) { // expected-note {{Taking false branch}} + P->foo(); // should have no warning here, path is impossible + } + P.reset(); // expected-note {{Smart pointer 'P' reset using a null value}} + // Now P is null + if (!P) { + // expected-note@-1 {{Taking true branch}} + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1{{Dereference of null smart pointer 'P'}} + } +} + +#endif Index: clang/test/Analysis/Inputs/system-header-simulator-cxx.h =================================================================== --- clang/test/Analysis/Inputs/system-header-simulator-cxx.h +++ clang/test/Analysis/Inputs/system-header-simulator-cxx.h @@ -978,6 +978,17 @@ void swap(unique_ptr<T> &x, unique_ptr<T> &y) noexcept { x.swap(y); } + +template <class T, class... Args> +unique_ptr<T> make_unique(Args &&...args); + +#if __cplusplus >= 202002L + +template <class T> +unique_ptr<T> make_unique_for_overwrite(); + +#endif + } // namespace std #endif Index: clang/lib/StaticAnalyzer/Core/SValBuilder.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ clang/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -192,12 +192,19 @@ const LocationContext *LCtx, unsigned VisitCount) { QualType T = E->getType(); - assert(Loc::isLocType(T)); - assert(SymbolManager::canSymbolicate(T)); - if (T->isNullPtrType()) - return makeZeroVal(T); + return getConjuredHeapSymbolVal(E, LCtx, T, VisitCount); +} + +DefinedOrUnknownSVal +SValBuilder::getConjuredHeapSymbolVal(const Expr *E, + const LocationContext *LCtx, + QualType type, unsigned VisitCount) { + assert(Loc::isLocType(type)); + assert(SymbolManager::canSymbolicate(type)); + if (type->isNullPtrType()) + return makeZeroVal(type); - SymbolRef sym = SymMgr.conjureSymbol(E, LCtx, T, VisitCount); + SymbolRef sym = SymMgr.conjureSymbol(E, LCtx, type, VisitCount); return loc::MemRegionVal(MemMgr.getSymbolicHeapRegion(sym)); } Index: clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp +++ clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp @@ -35,6 +35,7 @@ using namespace ento; namespace { + class SmartPtrModeling : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges, check::LiveSymbols> { @@ -76,6 +77,9 @@ {{"release"}, &SmartPtrModeling::handleRelease}, {{"swap", 1}, &SmartPtrModeling::handleSwap}, {{"get"}, &SmartPtrModeling::handleGet}}; + const CallDescription StdMakeUniqueCall{{"std", "make_unique"}}; + const CallDescription StdMakeUniqueForOverwriteCall{ + {"std", "make_unique_for_overwrite"}}; }; } // end of anonymous namespace @@ -135,6 +139,21 @@ return State; } +// This is for use with standalone-functions like std::make_unique, +// std::make_unique_for_overwrite, etc. It reads the template parameter and +// returns the pointer type corresponding to it, +static QualType getPointerTypeFromTemplateArg(const CallEvent &Call, + CheckerContext &C) { + const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); + if (!FD || !FD->isFunctionTemplateSpecialization()) + return {}; + const auto &TemplateArgs = FD->getTemplateSpecializationArgs()->asArray(); + if (TemplateArgs.size() == 0) + return {}; + auto ValueType = TemplateArgs[0].getAsType(); + return C.getASTContext().getPointerType(ValueType.getCanonicalType()); +} + // Helper method to get the inner pointer type of specialized smart pointer // Returns empty type if not found valid inner pointer type. static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) { @@ -177,7 +196,35 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call, CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + if (Call.isCalled(StdMakeUniqueCall) || + Call.isCalled(StdMakeUniqueForOverwriteCall)) { + const Optional<SVal> ThisRegionOpt = Call.getReturnValueUnderConstruction(); + if (!ThisRegionOpt) + return false; + + const auto PtrVal = C.getSValBuilder().getConjuredHeapSymbolVal( + Call.getOriginExpr(), C.getLocationContext(), + getPointerTypeFromTemplateArg(Call, C), C.blockCount()); + + const MemRegion *ThisRegion = ThisRegionOpt->getAsRegion(); + State = State->set<TrackedRegionMap>(ThisRegion, PtrVal); + State = State->assume(PtrVal, true); + + // TODO: ExprEngine should do this for us. + auto &Engine = State->getStateManager().getOwningEngine(); + State = Engine.updateObjectsUnderConstruction( + *ThisRegionOpt, nullptr, State, C.getLocationContext(), + Call.getConstructionContext(), {}); + + // We don't leave a note here since it is guaranteed the + // unique_ptr from this call is non-null (hence is safe to de-reference). + C.addTransition(State); + return true; + } + if (!smartptr::isStdSmartPtrCall(Call)) return false; Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h @@ -246,6 +246,14 @@ const LocationContext *LCtx, unsigned Count); + /// Conjure a symbol representing heap allocated memory region. + /// + /// Note, now, the expression *doesn't* need to represent a location. + /// But the type need to! + DefinedOrUnknownSVal getConjuredHeapSymbolVal(const Expr *E, + const LocationContext *LCtx, + QualType type, unsigned Count); + DefinedOrUnknownSVal getDerivedRegionValueSymbolVal( SymbolRef parentSymbol, const TypedValueRegion *region);
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits