Skip to content

[analyzer] Limit Store by region-store-binding-limit #127602

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,14 @@ ANALYZER_OPTION(
"behavior, set the option to 0.",
5)

ANALYZER_OPTION(
unsigned, RegionStoreMaxBindingFanOut, "region-store-max-binding-fanout",
"This option limits how many sub-bindings a single binding operation can "
"scatter into. For example, binding an array would scatter into binding "
"each individual element. Setting this to zero means unlimited, but then "
"modelling large array initializers may take proportional time to their "
"size.", 128)

//===----------------------------------------------------------------------===//
// String analyzer options.
//===----------------------------------------------------------------------===//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -659,13 +659,13 @@ class ExprEngine {
SVal Loc, SVal Val,
const LocationContext *LCtx);

public:
/// A simple wrapper when you only need to notify checkers of pointer-escape
/// of some values.
ProgramStateRef escapeValues(ProgramStateRef State, ArrayRef<SVal> Vs,
PointerEscapeKind K,
const CallEvent *Call = nullptr) const;

public:
// FIXME: 'tag' should be removed, and a LocationContext should be used
// instead.
// FIXME: Comment on the meaning of the arguments, when 'St' may not
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ class ProgramState : public llvm::FoldingSetNode {
/// makeWithStore - Return a ProgramState with the same values as the current
/// state with the exception of using the specified Store.
ProgramStateRef makeWithStore(const StoreRef &store) const;
ProgramStateRef makeWithStore(const BindResult &BindRes) const;

void setStore(const StoreRef &storeRef);

Expand Down
21 changes: 14 additions & 7 deletions clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ class SymbolReaper;

using InvalidatedSymbols = llvm::DenseSet<SymbolRef>;

struct BindResult {
StoreRef ResultingStore;

// If during the bind operation we exhaust the allowed binding budget, we set
// this to the beginning of the escaped part of the region.
llvm::SmallVector<SVal, 0> FailedToBindValues;
};

class StoreManager {
protected:
SValBuilder &svalBuilder;
Expand Down Expand Up @@ -105,17 +113,17 @@ class StoreManager {
/// \return A StoreRef object that contains the same
/// bindings as \c store with the addition of having the value specified
/// by \c val bound to the location given for \c loc.
virtual StoreRef Bind(Store store, Loc loc, SVal val) = 0;
virtual BindResult Bind(Store store, Loc loc, SVal val) = 0;

/// Return a store with the specified value bound to all sub-regions of the
/// region. The region must not have previous bindings. If you need to
/// invalidate existing bindings, consider invalidateRegions().
virtual StoreRef BindDefaultInitial(Store store, const MemRegion *R,
SVal V) = 0;
virtual BindResult BindDefaultInitial(Store store, const MemRegion *R,
SVal V) = 0;

/// Return a store with in which all values within the given region are
/// reset to zero. This method is allowed to overwrite previous bindings.
virtual StoreRef BindDefaultZero(Store store, const MemRegion *R) = 0;
virtual BindResult BindDefaultZero(Store store, const MemRegion *R) = 0;

/// Create a new store with the specified binding removed.
/// \param ST the original store, that is the basis for the new store.
Expand Down Expand Up @@ -240,9 +248,8 @@ class StoreManager {

/// enterStackFrame - Let the StoreManager to do something when execution
/// engine is about to execute into a callee.
StoreRef enterStackFrame(Store store,
const CallEvent &Call,
const StackFrameContext *CalleeCtx);
BindResult enterStackFrame(Store store, const CallEvent &Call,
const StackFrameContext *CalleeCtx);

/// Finds the transitive closure of symbols within the given region.
///
Expand Down
38 changes: 25 additions & 13 deletions clang/lib/StaticAnalyzer/Core/ProgramState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,32 +116,33 @@ ProgramStateRef ProgramState::bindLoc(Loc LV,
const LocationContext *LCtx,
bool notifyChanges) const {
ProgramStateManager &Mgr = getStateManager();
ProgramStateRef newState = makeWithStore(Mgr.StoreMgr->Bind(getStore(),
LV, V));
ExprEngine &Eng = Mgr.getOwningEngine();
ProgramStateRef State = makeWithStore(Mgr.StoreMgr->Bind(getStore(), LV, V));
const MemRegion *MR = LV.getAsRegion();

if (MR && notifyChanges)
return Mgr.getOwningEngine().processRegionChange(newState, MR, LCtx);
return Eng.processRegionChange(State, MR, LCtx);

return newState;
return State;
}

ProgramStateRef
ProgramState::bindDefaultInitial(SVal loc, SVal V,
const LocationContext *LCtx) const {
ProgramStateManager &Mgr = getStateManager();
const MemRegion *R = loc.castAs<loc::MemRegionVal>().getRegion();
const StoreRef &newStore = Mgr.StoreMgr->BindDefaultInitial(getStore(), R, V);
ProgramStateRef new_state = makeWithStore(newStore);
return Mgr.getOwningEngine().processRegionChange(new_state, R, LCtx);
BindResult BindRes = Mgr.StoreMgr->BindDefaultInitial(getStore(), R, V);
ProgramStateRef State = makeWithStore(BindRes);
return Mgr.getOwningEngine().processRegionChange(State, R, LCtx);
}

ProgramStateRef
ProgramState::bindDefaultZero(SVal loc, const LocationContext *LCtx) const {
ProgramStateManager &Mgr = getStateManager();
const MemRegion *R = loc.castAs<loc::MemRegionVal>().getRegion();
const StoreRef &newStore = Mgr.StoreMgr->BindDefaultZero(getStore(), R);
ProgramStateRef new_state = makeWithStore(newStore);
return Mgr.getOwningEngine().processRegionChange(new_state, R, LCtx);
BindResult BindRes = Mgr.StoreMgr->BindDefaultZero(getStore(), R);
ProgramStateRef State = makeWithStore(BindRes);
return Mgr.getOwningEngine().processRegionChange(State, R, LCtx);
}

typedef ArrayRef<const MemRegion *> RegionList;
Expand Down Expand Up @@ -232,9 +233,8 @@ SVal ProgramState::wrapSymbolicRegion(SVal Val) const {
ProgramStateRef
ProgramState::enterStackFrame(const CallEvent &Call,
const StackFrameContext *CalleeCtx) const {
const StoreRef &NewStore =
getStateManager().StoreMgr->enterStackFrame(getStore(), Call, CalleeCtx);
return makeWithStore(NewStore);
return makeWithStore(
getStateManager().StoreMgr->enterStackFrame(getStore(), Call, CalleeCtx));
}

SVal ProgramState::getSelfSVal(const LocationContext *LCtx) const {
Expand Down Expand Up @@ -437,6 +437,18 @@ ProgramStateRef ProgramState::makeWithStore(const StoreRef &store) const {
return getStateManager().getPersistentState(NewSt);
}

ProgramStateRef ProgramState::makeWithStore(const BindResult &BindRes) const {
ExprEngine &Eng = getStateManager().getOwningEngine();
ProgramStateRef State = makeWithStore(BindRes.ResultingStore);

// We must always notify the checkers for failing binds because otherwise they
// may keep stale traits for these symbols.
// Eg., Malloc checker may report leaks if we failed to bind that symbol.
if (BindRes.FailedToBindValues.empty())
return State;
return Eng.escapeValues(State, BindRes.FailedToBindValues, PSK_EscapeOnBind);
}

ProgramStateRef ProgramState::cloneAsPosteriorlyOverconstrained() const {
ProgramState NewSt(*this);
NewSt.PosteriorlyOverconstrained = true;
Expand Down
Loading