Skip to content

Commit ceb639d

Browse files
committed
[analyzer] Fix invalidation when returning into a ctor initializer.
Due to RVO the target region of a function that returns an object by value isn't necessarily a temporary object region; it may be an arbitrary memory region. In particular, it may be a field of a bigger object. Make sure we don't invalidate the bigger object when said function is evaluated conservatively. Differential Revision: https://reviews.llvm.org/D63968 llvm-svn: 364870
1 parent 512f483 commit ceb639d

File tree

2 files changed

+37
-5
lines changed

2 files changed

+37
-5
lines changed

clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp

+12-5
Original file line numberDiff line numberDiff line change
@@ -634,12 +634,19 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call,
634634
std::tie(State, Target) =
635635
prepareForObjectConstruction(Call.getOriginExpr(), State, LCtx,
636636
RTC->getConstructionContext(), CallOpts);
637-
assert(Target.getAsRegion());
638-
// Invalidate the region so that it didn't look uninitialized. Don't notify
639-
// the checkers.
640-
State = State->invalidateRegions(Target.getAsRegion(), E, Count, LCtx,
637+
const MemRegion *TargetR = Target.getAsRegion();
638+
assert(TargetR);
639+
// Invalidate the region so that it didn't look uninitialized. If this is
640+
// a field or element constructor, we do not want to invalidate
641+
// the whole structure. Pointer escape is meaningless because
642+
// the structure is a product of conservative evaluation
643+
// and therefore contains nothing interesting at this point.
644+
RegionAndSymbolInvalidationTraits ITraits;
645+
ITraits.setTrait(TargetR,
646+
RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion);
647+
State = State->invalidateRegions(TargetR, E, Count, LCtx,
641648
/* CausedByPointerEscape=*/false, nullptr,
642-
&Call, nullptr);
649+
&Call, &ITraits);
643650

644651
R = State->getSVal(Target.castAs<Loc>(), E->getType());
645652
} else {

clang/test/Analysis/rvo.cpp

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker core,cplusplus \
2+
// RUN: -analyzer-checker debug.ExprInspection -verify %s
3+
4+
void clang_analyzer_eval(bool);
5+
6+
struct A {
7+
int x;
8+
};
9+
10+
A getA();
11+
12+
struct B {
13+
int *p;
14+
A a;
15+
16+
B(int *p) : p(p), a(getA()) {}
17+
};
18+
19+
void foo() {
20+
B b1(nullptr);
21+
clang_analyzer_eval(b1.p == nullptr); // expected-warning{{TRUE}}
22+
B b2(new int); // No leak yet!
23+
clang_analyzer_eval(b2.p == nullptr); // expected-warning{{FALSE}}
24+
// expected-warning@-1{{Potential leak of memory pointed to by 'b2.p'}}
25+
}

0 commit comments

Comments
 (0)