Skip to content

Commit

Permalink
[analyzer] Keep track of escaped locals
Browse files Browse the repository at this point in the history
We want to escape all symbols that are stored into escaped regions.
The problem is, we did not know which local regions were escaped. Until now.
This should fix some false positives like the one in the tests.

Differential Revision: https://reviews.llvm.org/D71152
  • Loading branch information
Xazax-hun committed Dec 10, 2019
1 parent a6e50e4 commit f3a2820
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 12 deletions.
Expand Up @@ -627,6 +627,9 @@ class ExprEngine : public SubEngine {
const CallEvent *Call,
RegionAndSymbolInvalidationTraits &ITraits) override;

ProgramStateRef processLocalRegionEscape(ProgramStateRef State,
const MemRegion *R) const override;

/// A simple wrapper when you only need to notify checkers of pointer-escape
/// of a single value.
ProgramStateRef escapeValue(ProgramStateRef State, SVal V,
Expand Down
Expand Up @@ -27,7 +27,7 @@

namespace llvm {
class APSInt;
}
} // namespace llvm

namespace clang {
class ASTContext;
Expand Down Expand Up @@ -872,8 +872,8 @@ class ScanReachableSymbols {
bool scan(const SymExpr *sym);
};

} // end ento namespace
} // namespace ento

} // end clang namespace
} // namespace clang

#endif
Expand Up @@ -149,14 +149,16 @@ class SubEngine {
}

virtual ProgramStateRef
processPointerEscapedOnBind(ProgramStateRef State, SVal Loc, SVal Val, const LocationContext *LCtx) = 0;
processPointerEscapedOnBind(ProgramStateRef State, SVal Loc, SVal Val,
const LocationContext *LCtx) = 0;

virtual ProgramStateRef notifyCheckersOfPointerEscape(
ProgramStateRef State, const InvalidatedSymbols *Invalidated,
ArrayRef<const MemRegion *> ExplicitRegions, const CallEvent *Call,
RegionAndSymbolInvalidationTraits &HTraits) = 0;

virtual ProgramStateRef
notifyCheckersOfPointerEscape(ProgramStateRef State,
const InvalidatedSymbols *Invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
const CallEvent *Call,
RegionAndSymbolInvalidationTraits &HTraits) = 0;
processLocalRegionEscape(ProgramStateRef State, const MemRegion *R) const = 0;

/// printJson - Called by ProgramStateManager to print checker-specific data.
virtual void printJson(raw_ostream &Out, ProgramStateRef State,
Expand Down
18 changes: 16 additions & 2 deletions clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
Expand Up @@ -193,6 +193,8 @@ typedef llvm::ImmutableMap<ConstructedObjectKey, SVal>
REGISTER_TRAIT_WITH_PROGRAMSTATE(ObjectsUnderConstruction,
ObjectsUnderConstructionMap)

REGISTER_SET_WITH_PROGRAMSTATE(EscapedLocals, const MemRegion *)

//===----------------------------------------------------------------------===//
// Engine construction and deletion.
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -723,6 +725,12 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out,
SymReaper.markLive(MR);
}

EscapedLocalsTy EscapedRegions = CleanedState->get<EscapedLocals>();
for (const MemRegion *MR : EscapedRegions) {
if (!SymReaper.isLiveRegion(MR))
CleanedState = CleanedState->remove<EscapedLocals>(MR);
}

getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper);

// Create a state in which dead bindings are removed from the environment
Expand Down Expand Up @@ -1194,6 +1202,11 @@ ProgramStateRef ExprEngine::escapeValue(ProgramStateRef State, SVal V,
State, Scanner.getSymbols(), /*CallEvent*/ nullptr, K, nullptr);
}

ProgramStateRef ExprEngine::processLocalRegionEscape(ProgramStateRef State,
const MemRegion *R) const {
return State->add<EscapedLocals>(R);
}

void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
ExplodedNodeSet &DstTop) {
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
Expand Down Expand Up @@ -2680,7 +2693,8 @@ void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred,

// A value escapes in four possible cases:
// (1) We are binding to something that is not a memory region.
// (2) We are binding to a MemRegion that does not have stack storage.
// (2) We are binding to a MemRegion that does not have stack storage
// or the stack storage is escaped.
// (3) We are binding to a top-level parameter region with a non-trivial
// destructor. We won't see the destructor during analysis, but it's there.
// (4) We are binding to a MemRegion with stack storage that the store
Expand All @@ -2691,7 +2705,7 @@ ExprEngine::processPointerEscapedOnBind(ProgramStateRef State, SVal Loc,

// Cases (1) and (2).
const MemRegion *MR = Loc.getAsRegion();
if (!MR || !MR->hasStackStorage())
if (!MR || !MR->hasStackStorage() || State->contains<EscapedLocals>(MR))
return escapeValue(State, Val, PSK_EscapeOnBind);

// Case (3).
Expand Down
10 changes: 9 additions & 1 deletion clang/lib/StaticAnalyzer/Core/ProgramState.cpp
Expand Up @@ -41,7 +41,8 @@ void ProgramStateRelease(const ProgramState *state) {
Mgr.freeStates.push_back(s);
}
}
}}
} // namespace ento
} // namespace clang

ProgramState::ProgramState(ProgramStateManager *mgr, const Environment& env,
StoreRef st, GenericDataMap gdm)
Expand Down Expand Up @@ -209,6 +210,13 @@ ProgramState::invalidateRegionsImpl(ValueList Values,
ProgramStateRef newState = makeWithStore(newStore);

if (CausedByPointerEscape) {
for (const MemRegion *R : Invalidated) {
if (!R->hasStackStorage())
continue;

newState = Eng.processLocalRegionEscape(newState, R->getBaseRegion());
}

newState = Eng.notifyCheckersOfPointerEscape(newState, IS,
TopLevelInvalidated,
Call,
Expand Down
9 changes: 9 additions & 0 deletions clang/test/Analysis/symbol-escape.cpp
Expand Up @@ -31,3 +31,12 @@ C **indirect_escape_in_bitwise_op() {
return Baz;
}

void save_ptr(int **);
void delete_saved();

void store_to_escaped_region() {
int *p;
save_ptr(&p);
p = new int;
delete_saved();
} // no-warning

0 comments on commit f3a2820

Please sign in to comment.