Skip to content

Commit c877294

Browse files
authored
[analyzer] Wrap SymbolicRegions by ElementRegions before getting a FieldRegion (#85211)
Inside the ExprEngine when we process the initializers, we create a PostInitializer program-point, which will refer to the field being initialized, see `FieldLoc` inside `ExprEngine::ProcessInitializer`. When a constructor (of which we evaluate the initializer-list) is analyzed in top-level context, then the `this` pointer will be represented by a `SymbolicRegion`, (as it should be). This means that we will form a `FieldRegion{SymbolicRegion{.}}` as the initialized region. ```c++ class Bear { public: void brum() const; }; class Door { public: // PostInitializer would refer to "FieldRegion{SymRegion{this}}" // whereas in the store and everywhere else it would be: // "FieldRegion{ELementRegion{SymRegion{Ty*, this}, 0, Ty}". Door() : ptr(nullptr) { ptr->brum(); // Bug } private: Bear* ptr; }; ``` We (as CSA folks) decided to avoid the creation of FieldRegions directly of symbolic regions in the past: f8643a9 --- In this patch, I propose to also canonicalize it as in the mentioned patch, into this: `FieldRegion{ElementRegion{SymbolicRegion{Ty*, .}, 0, Ty}` This would mean that FieldRegions will/should never simply wrap a SymbolicRegion directly, but rather an ElementRegion that is sitting in between. This patch should have practically no observable effects, as the store (due to the mentioned patch) was made resilient to this issue, but we use `PostInitializer::getLocationValue()` for an alternative reporting, where we faced this issue. Note that in really rare cases it suppresses now dereference bugs, as demonstrated in the test. It is because in the past we failed to follow the region of the PostInitializer inside the StoreSiteFinder visitor - because it was using this code: ```c++ // If this is a post initializer expression, initializing the region, we // should track the initializer expression. if (std::optional<PostInitializer> PIP = Pred->getLocationAs<PostInitializer>()) { const MemRegion *FieldReg = (const MemRegion *)PIP->getLocationValue(); if (FieldReg == R) { StoreSite = Pred; InitE = PIP->getInitializer()->getInit(); } } ``` Notice that the equality check didn't pass for the regions I'm canonicalizing in this patch. Given the nature of this change, we would rather upstream this patch. CPP-4954
1 parent 797336b commit c877294

File tree

3 files changed

+51
-14
lines changed

3 files changed

+51
-14
lines changed

clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,8 @@ class ProgramState : public llvm::FoldingSetNode {
494494
InvalidatedSymbols *IS,
495495
RegionAndSymbolInvalidationTraits *HTraits,
496496
const CallEvent *Call) const;
497+
498+
SVal wrapSymbolicRegion(SVal Base) const;
497499
};
498500

499501
//===----------------------------------------------------------------------===//
@@ -782,20 +784,6 @@ inline SVal ProgramState::getLValue(const ObjCIvarDecl *D, SVal Base) const {
782784
return getStateManager().StoreMgr->getLValueIvar(D, Base);
783785
}
784786

785-
inline SVal ProgramState::getLValue(const FieldDecl *D, SVal Base) const {
786-
return getStateManager().StoreMgr->getLValueField(D, Base);
787-
}
788-
789-
inline SVal ProgramState::getLValue(const IndirectFieldDecl *D,
790-
SVal Base) const {
791-
StoreManager &SM = *getStateManager().StoreMgr;
792-
for (const auto *I : D->chain()) {
793-
Base = SM.getLValueField(cast<FieldDecl>(I), Base);
794-
}
795-
796-
return Base;
797-
}
798-
799787
inline SVal ProgramState::getLValue(QualType ElementType, SVal Idx, SVal Base) const{
800788
if (std::optional<NonLoc> N = Idx.getAs<NonLoc>())
801789
return getStateManager().StoreMgr->getLValueElement(ElementType, *N, Base);

clang/lib/StaticAnalyzer/Core/ProgramState.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,20 @@ ProgramStateRef ProgramState::killBinding(Loc LV) const {
226226
return makeWithStore(newStore);
227227
}
228228

229+
/// SymbolicRegions are expected to be wrapped by an ElementRegion as a
230+
/// canonical representation. As a canonical representation, SymbolicRegions
231+
/// should be wrapped by ElementRegions before getting a FieldRegion.
232+
/// See f8643a9b31c4029942f67d4534c9139b45173504 why.
233+
SVal ProgramState::wrapSymbolicRegion(SVal Val) const {
234+
const auto *BaseReg = dyn_cast_or_null<SymbolicRegion>(Val.getAsRegion());
235+
if (!BaseReg)
236+
return Val;
237+
238+
StoreManager &SM = getStateManager().getStoreManager();
239+
QualType ElemTy = BaseReg->getPointeeStaticType();
240+
return loc::MemRegionVal{SM.GetElementZeroRegion(BaseReg, ElemTy)};
241+
}
242+
229243
ProgramStateRef
230244
ProgramState::enterStackFrame(const CallEvent &Call,
231245
const StackFrameContext *CalleeCtx) const {
@@ -451,6 +465,24 @@ void ProgramState::setStore(const StoreRef &newStore) {
451465
store = newStoreStore;
452466
}
453467

468+
SVal ProgramState::getLValue(const FieldDecl *D, SVal Base) const {
469+
Base = wrapSymbolicRegion(Base);
470+
return getStateManager().StoreMgr->getLValueField(D, Base);
471+
}
472+
473+
SVal ProgramState::getLValue(const IndirectFieldDecl *D, SVal Base) const {
474+
StoreManager &SM = *getStateManager().StoreMgr;
475+
Base = wrapSymbolicRegion(Base);
476+
477+
// FIXME: This should work with `SM.getLValueField(D->getAnonField(), Base)`,
478+
// but that would break some tests. There is probably a bug somewhere that it
479+
// would expose.
480+
for (const auto *I : D->chain()) {
481+
Base = SM.getLValueField(cast<FieldDecl>(I), Base);
482+
}
483+
return Base;
484+
}
485+
454486
//===----------------------------------------------------------------------===//
455487
// State pretty-printing.
456488
//===----------------------------------------------------------------------===//

clang/test/Analysis/inlining/false-positive-suppression.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,3 +210,20 @@ namespace Cleanups {
210210
testArgumentHelper(NonTrivial().getNull());
211211
}
212212
}
213+
214+
class Bear *getNullBear() { return nullptr; }
215+
class Bear {
216+
public:
217+
void brum() const;
218+
};
219+
class Door {
220+
public:
221+
Door() : ptr(getNullBear()) {
222+
ptr->brum();
223+
#ifndef SUPPRESSED
224+
// expected-warning@-2 {{Called C++ object pointer is null}}
225+
#endif
226+
}
227+
private:
228+
Bear* ptr;
229+
};

0 commit comments

Comments
 (0)