Skip to content

Commit f9e0fa8

Browse files
[analyzer] MoveChecker: correct invalidation of this-regions (#169626)
By completely omitting invalidation in the case of InstanceCall, we do not clear the moved state of the fields of the this object after an opaque call to a member function of the object itself.
1 parent 49496c5 commit f9e0fa8

File tree

2 files changed

+59
-9
lines changed

2 files changed

+59
-9
lines changed

clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -244,11 +244,12 @@ bool isMovedFrom(ProgramStateRef State, const MemRegion *Region) {
244244

245245
// If a region is removed all of the subregions needs to be removed too.
246246
static ProgramStateRef removeFromState(ProgramStateRef State,
247-
const MemRegion *Region) {
247+
const MemRegion *Region,
248+
bool Strict = false) {
248249
if (!Region)
249250
return State;
250251
for (auto &E : State->get<TrackedRegionMap>()) {
251-
if (E.first->isSubRegionOf(Region))
252+
if ((!Strict || E.first != Region) && E.first->isSubRegionOf(Region))
252253
State = State->remove<TrackedRegionMap>(E.first);
253254
}
254255
return State;
@@ -709,18 +710,16 @@ ProgramStateRef MoveChecker::checkRegionChanges(
709710
// that are passed directly via non-const pointers or non-const references
710711
// or rvalue references.
711712
// In case of an InstanceCall don't invalidate the this-region since
712-
// it is fully handled in checkPreCall and checkPostCall.
713-
const MemRegion *ThisRegion = nullptr;
714-
if (const auto *IC = dyn_cast<CXXInstanceCall>(Call))
715-
ThisRegion = IC->getCXXThisVal().getAsRegion();
713+
// it is fully handled in checkPreCall and checkPostCall, but do invalidate
714+
// its strict subregions, as they are not handled.
716715

717716
// Requested ("explicit") regions are the regions passed into the call
718717
// directly, but not all of them end up being invalidated.
719718
// But when they do, they appear in the InvalidatedRegions array as well.
720719
for (const auto *Region : RequestedRegions) {
721-
if (ThisRegion != Region &&
722-
llvm::is_contained(InvalidatedRegions, Region))
723-
State = removeFromState(State, Region);
720+
if (llvm::is_contained(InvalidatedRegions, Region))
721+
State = removeFromState(State, Region,
722+
/*Strict=*/isa<CXXInstanceCall>(Call));
724723
}
725724
} else {
726725
// For invalidations that aren't caused by calls, assume nothing. In
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move %s -std=c++11\
2+
// RUN: -analyzer-output=text
3+
4+
#include "Inputs/system-header-simulator-cxx.h"
5+
6+
class C {
7+
int n;
8+
public:
9+
void memberFun();
10+
};
11+
12+
template <class... Ts> void opaqueFreeFun(Ts...);
13+
14+
class Foo {
15+
std::unique_ptr<C> up;
16+
void opaqueMemberFun();
17+
18+
void testOpaqueMemberFun() {
19+
auto tmp = std::move(up);
20+
opaqueMemberFun(); // clears region state
21+
(void)up->memberFun(); // no-warning
22+
}
23+
24+
void testOpaqueFreeFun() {
25+
auto tmp = std::move(up); // expected-note{{Smart pointer 'up' of type 'std::unique_ptr' is reset to null when moved from}}
26+
opaqueFreeFun(); // does not clear region state
27+
(void)up->memberFun(); // expected-warning{{Dereference of null smart pointer 'up' of type 'std::unique_ptr'}}
28+
// expected-note@-1{{Dereference of null smart pointer 'up' of type 'std::unique_ptr'}}
29+
}
30+
};
31+
32+
void testInstanceMemberFun(C c) {
33+
auto tmp1 = std::move(c); // expected-note{{Object 'c' is moved}}
34+
auto tmp2 = std::move(c); // expected-warning{{Moved-from object 'c' is moved}}
35+
// expected-note@-1{{Moved-from object 'c' is moved}}
36+
auto tmp3 = std::move(c);
37+
c.memberFun(); // does not clear region state
38+
auto tmp4 = std::move(c);
39+
auto tmp5 = std::move(c); // no-warning
40+
}
41+
42+
void testPassByRef(C c) {
43+
auto tmp1 = std::move(c); // expected-note{{Object 'c' is moved}}
44+
auto tmp2 = std::move(c); // expected-warning{{Moved-from object 'c' is moved}}
45+
// expected-note@-1{{Moved-from object 'c' is moved}}
46+
auto tmp3 = std::move(c);
47+
opaqueFreeFun<C&>(c); // clears region state
48+
auto tmp4 = std::move(c); // expected-note{{Object 'c' is moved}}
49+
auto tmp5 = std::move(c); // expected-warning{{Moved-from object 'c' is moved}}
50+
// expected-note@-1{{Moved-from object 'c' is moved}}
51+
}

0 commit comments

Comments
 (0)