================ @@ -0,0 +1,339 @@ +//===- DestroyedAsWrongTypeChecker.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 storage reused by placement new for an object +// of a different type and then destroyed or deleted as the original type +// (C++ [basic.life]p11, [expr.delete]p3). +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ExprCXX.h" +#include "clang/Analysis/CFG.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" +#include <optional> + +using namespace clang; +using namespace ento; + +namespace { +struct WrongDestruction { + QualType Placed; + const CXXRecordDecl *DtorClass; + const CXXDeleteExpr *Delete; + + bool operator==(const WrongDestruction &X) const { + return Placed == X.Placed && DtorClass == X.DtorClass && Delete == X.Delete; + } + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.Add(Placed); + ID.AddPointer(DtorClass); + ID.AddPointer(Delete); + } +}; +} // namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(PlacedObjectTypes, const MemRegion *, QualType) +REGISTER_SET_WITH_PROGRAMSTATE(NewAllocatedObjects, const MemRegion *) +REGISTER_MAP_WITH_PROGRAMSTATE(PendingDestructions, const MemRegion *, + WrongDestruction) + +namespace { +class DestroyedAsWrongTypeChecker + : public Checker<check::PostStmt<CXXNewExpr>, check::PreCall, + check::PostCall, check::DeadSymbols> { + const BugType BT{this, "Object destroyed as a type it no longer has", + categories::MemoryError}; + + void reportBug(const MemRegion *R, const WrongDestruction &W, + CheckerContext &C) const; + void reportOrDefer(const MemRegion *R, const WrongDestruction &W, + CheckerContext &C) const; + +public: + void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; +}; +} // namespace + +// An array is keyed by its first element, the object its destructor gets. +static const MemRegion *getStorageRegion(SVal V, CheckerContext &C) { + const MemRegion *R = V.getAsRegion(); + if (!R) + return nullptr; + R = R->StripCasts(); + ASTContext &Ctx = C.getASTContext(); + while (const auto *TR = dyn_cast<TypedValueRegion>(R)) { + const ArrayType *AT = Ctx.getAsArrayType(TR->getValueType()); + if (!AT) + break; + R = C.getStoreManager().getRegionManager().getElementRegion( + AT->getElementType(), C.getSValBuilder().makeZeroArrayIndex(), TR, Ctx); + } + return R; +} + +// Storage obtained as raw memory has no original object to be replaced. +static bool holdsOriginalObject(ProgramStateRef State, const MemRegion *R) { + if (State->contains<NewAllocatedObjects>(R)) + return true; + const auto *TR = dyn_cast<TypedValueRegion>(R); + if (!TR) + return false; + // Placement new into a union starts the lifetime of one of its members. + const CXXRecordDecl *RD = TR->getValueType()->getAsCXXRecordDecl(); + return RD && !RD->isUnion() && RD->hasNonTrivialDestructor(); +} + +static bool isSameRecord(QualType T, const CXXRecordDecl *RD) { + const CXXRecordDecl *TD = T->getAsCXXRecordDecl(); + return TD && TD->getCanonicalDecl() == RD->getCanonicalDecl(); +} + +static QualType getStorageType(const MemRegion *R) { + if (const auto *TR = dyn_cast<TypedValueRegion>(R)) + return TR->getValueType(); + if (const auto *SR = dyn_cast<SymbolicRegion>(R)) + return SR->getPointeeStaticType(); + return {}; +} + +// A field and a byte offset from its parent are different regions. +static bool isSameLocation(const MemRegion *A, const MemRegion *B) { + RegionOffset OA = A->getAsOffset(); + RegionOffset OB = B->getAsOffset(); + return OA.isValid() && OB.isValid() && !OA.hasSymbolicOffset() && + !OB.hasSymbolicOffset() && OA.getRegion() == OB.getRegion() && + OA.getOffset() == OB.getOffset(); +} + +static ProgramStateRef forgetPlacedTypes(ProgramStateRef State, + const MemRegion *R) { + for (const MemRegion *Key : + llvm::make_first_range(State->get<PlacedObjectTypes>())) + if (Key == R || Key->isSubRegionOf(R) || isSameLocation(Key, R)) ---------------- NagyDonat wrote:
At first I thought that `Key == R` is just a performance optimization, but in fact it is significant, because `isSameLocation(Key, R)` can return false when the offsets are symbolic. Please move this equality check into `isSameLocation` (as an `if (A == B) return true;` check at the beginning) to make the name of that function more accurate. https://github.com/llvm/llvm-project/pull/237801 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
