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);
using namespace ento;
namespace {
+
class SmartPtrModeling
: public Checker<eval::Call, check::DeadSymbols, check::RegionChanges,
check::LiveSymbols> {
{{"swap", 1}, &SmartPtrModeling::handleSwapMethod},
{{"get"}, &SmartPtrModeling::handleGet}};
const CallDescription StdSwapCall{{"std", "swap"}, 2};
+ const CallDescription StdMakeUniqueCall{{"std", "make_unique"}};
+ const CallDescription StdMakeUniqueForOverwriteCall{
+ {"std", "make_unique_for_overwrite"}};
};
} // end of anonymous namespace
return {};
auto TemplateArgs = TSD->getTemplateArgs().asArray();
- if (TemplateArgs.size() == 0)
+ if (TemplateArgs.empty())
return {};
auto InnerValueType = TemplateArgs[0].getAsType();
return C.getASTContext().getPointerType(InnerValueType.getCanonicalType());
}
+// 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) {
bool SmartPtrModeling::evalCall(const CallEvent &Call,
CheckerContext &C) const {
+
ProgramStateRef State = C.getState();
// If any one of the arg is a unique_ptr, then
return handleSwap(State, Call.getArgSVal(0), Call.getArgSVal(1), C);
}
+ if (Call.isCalled(StdMakeUniqueCall) ||
+ Call.isCalled(StdMakeUniqueForOverwriteCall)) {
+ if (!ModelSmartPtrDereference)
+ return false;
+
+ 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.
+ // For a bit more context:
+ // 1) Why do we need this? Since we are modelling a "function"
+ // that returns a constructed object we need to store this information in
+ // the program state.
+ //
+ // 2) Why does this work?
+ // `updateObjectsUnderConstruction` does exactly as it sounds.
+ //
+ // 3) How should it look like when moved to the Engine?
+ // It would be nice if we can just
+ // pretend we don't need to know about this - ie, completely automatic work.
+ // However, realistically speaking, I think we would need to "signal" the
+ // ExprEngine evalCall handler that we are constructing an object with this
+ // function call (constructors obviously construct, hence can be
+ // automatically deduced).
+ 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;
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));
}
template <typename T>
bool operator<=(nullptr_t x, const unique_ptr<T> &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
// RUN: %clang_analyze_cc1\
-// RUN: -analyzer-checker=core,cplusplus.Move,alpha.cplusplus.SmartPtr\
+// RUN: -analyzer-checker=core,cplusplus.Move,alpha.cplusplus.SmartPtr,debug.ExprInspection\
+// 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,debug.ExprInspection\
// RUN: -analyzer-config cplusplus.SmartPtrModeling:ModelSmartPtrDereference=true\
// RUN: -analyzer-output=text -std=c++11 %s -verify=expected
#include "Inputs/system-header-simulator-cxx.h"
+void clang_analyzer_eval(bool);
+
class A {
public:
A(){};
// 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
+
+struct G {
+ int *p;
+ G(int *p): p(p) {}
+ ~G() { *p = 0; }
+};
+
+void foo() {
+ int x = 1;
+ {
+ auto P = std::make_unique<G>(&x);
+ // FIXME: There should not be a state split here, it should take the true path.
+ clang_analyzer_eval(*P->p == 1); // expected-warning {{TRUE}}
+ // expected-warning@-1 {{FALSE}}
+ // expected-note@-2 {{Assuming the condition is true}}
+ // expected-note@-3 {{Assuming the condition is false}}
+ // expected-note@-4 {{TRUE}}
+ // expected-note@-5 {{FALSE}}
+ // expected-note@-6 {{Assuming the condition is false}}
+ }
+ // FIXME: Should be fixed when unique_ptr desctructors are
+ // properly modelled. This includes modelling the call to
+ // the destructor of the inner pointer type.
+ clang_analyzer_eval(x == 0); // expected-warning {{FALSE}}
+ // expected-note@-1 {{FALSE}}
+}