================ @@ -0,0 +1,438 @@ +//===- PlacementSelfCopyChecker.cpp -----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines a checker that finds placement new expressions whose initializer +// reads a part of the target storage that has already been overwritten, e.g. +// `::new (p) T(*p)`. A copy or move constructor that is not inlined is +// reported whenever its source is the target storage. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ExprCXX.h" +#include "clang/AST/ParentMap.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +using PlacementKey = std::pair<const CXXNewExpr *, const StackFrame *>; +REGISTER_MAP_WITH_PROGRAMSTATE(PlacementTargets, PlacementKey, + const MemRegion *) +REGISTER_SET_WITH_PROGRAMSTATE(SelfSourcedTargets, const MemRegion *) +REGISTER_SET_WITH_PROGRAMSTATE(OverwrittenParts, const MemRegion *) +REGISTER_TRAIT_WITH_PROGRAMSTATE(PendingSelfCopy, const MemRegion *) +REGISTER_TRAIT_WITH_PROGRAMSTATE(ReadOverwritten, const MemRegion *) + +namespace { +class PlacementSelfCopyChecker + : public Checker<check::PostCall, check::PreCall, check::Bind, + check::Location, check::PostStmt<CXXNewExpr>> { + const BugType BT{this, "Placement new initialized from its own storage", + categories::MemoryError}; + const BugType ReadBT{this, "Placement new reads overwritten storage", + categories::MemoryError}; + + void reportRead(const Expr *E, const MemRegion *Overwritten, + CheckerContext &C) const; + void reportSelfCopy(const Expr *Arg, CheckerContext &C) const; + void reportPendingSelfCopy(const CallEvent &Call, CheckerContext &C) const; + void checkOverwrittenRead(const Stmt *S, const Expr *E, + const MemRegion *Source, CheckerContext &C) const; + +public: + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkBind(SVal Loc, SVal Val, const Stmt *S, bool AtDeclInit, + CheckerContext &C) const; + void checkLocation(SVal Loc, bool IsLoad, const Stmt *S, + CheckerContext &C) const; + void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const; +}; +} // namespace + +namespace { +struct Extent { + const MemRegion *Base; + int64_t Begin; + std::optional<int64_t> End; +}; +} // namespace + +static std::optional<Extent> getExtent(const MemRegion *R, ASTContext &Ctx) { + RegionOffset Offset = R->getAsOffset(); + if (!Offset.isValid() || Offset.hasSymbolicOffset()) + return std::nullopt; + Extent E{Offset.getRegion(), Offset.getOffset(), std::nullopt}; + if (const auto *TR = dyn_cast<TypedValueRegion>(R)) { + QualType Ty = TR->getValueType(); + if (!Ty->isIncompleteType()) + E.End = E.Begin + Ctx.getTypeSize(Ty); + } + return E; +} + +static bool overlaps(const MemRegion *A, const MemRegion *B, ASTContext &Ctx) { + if (A == B || A->isSubRegionOf(B) || B->isSubRegionOf(A)) + return true; + std::optional<Extent> EA = getExtent(A, Ctx), EB = getExtent(B, Ctx); + if (!EA || !EB || EA->Base != EB->Base) + return false; + return (!EB->End || EA->Begin < *EB->End) && + (!EA->End || EB->Begin < *EA->End); +} + +static bool isWithin(const MemRegion *R, const MemRegion *Container, + ASTContext &Ctx) { + if (R == Container || R->isSubRegionOf(Container)) + return true; + std::optional<Extent> ER = getExtent(R, Ctx), EC = getExtent(Container, Ctx); + if (!ER || !EC || ER->Base != EC->Base || ER->Begin < EC->Begin) + return false; + return !EC->End || (ER->End && *ER->End <= *EC->End); +} + +static bool isThisBased(const Expr *E) { + while (E) { + E = E->IgnoreParenCasts(); + if (isa<CXXThisExpr>(E)) + return true; + if (const auto *ME = dyn_cast<MemberExpr>(E)) + E = ME->getBase(); + else if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) + E = ASE->getBase(); + else if (const auto *BO = dyn_cast<BinaryOperator>(E); + BO && BO->isAssignmentOp()) + E = BO->getLHS(); + else if (const auto *UO = dyn_cast<UnaryOperator>(E); + UO && UO->getOpcode() == UO_Deref) + E = UO->getSubExpr(); + else + return false; + } + return false; +} + +static const MemRegion *findSelfSourcedTarget(ProgramStateRef State, + const MemRegion *R, + CheckerContext &C) { + for (const MemRegion *Target : State->get<SelfSourcedTargets>()) + if (isWithin(R, Target, C.getASTContext())) + return Target; + return nullptr; +} + +static const MemRegion *findOverwritten(ProgramStateRef State, + const MemRegion *R, CheckerContext &C) { + for (const MemRegion *W : State->get<OverwrittenParts>()) + if (overlaps(R, W, C.getASTContext())) + return W; + return nullptr; +} + +// CSA binds an init list only after evaluating all elements; recompute order. +static const MemRegion *walkInitList(PlacementKey Key, const MemRegion *Target, ---------------- mark-zilber wrote:
Refactored whole code. https://github.com/llvm/llvm-project/pull/233525 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
