Skip to content

Commit c1b08dd

Browse files
committed
[𝘀𝗽𝗿] initial version
Created using spr 1.3.7
2 parents 0e0ec4c + 2d4147c commit c1b08dd

File tree

4 files changed

+549
-1
lines changed

4 files changed

+549
-1
lines changed

clang/include/clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "clang/ASTMatchers/ASTMatchers.h"
1414
#include "clang/Analysis/CFG.h"
1515
#include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
16+
#include "clang/Analysis/FlowSensitive/CachedConstAccessorsLattice.h"
1617
#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
1718
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
1819
#include "clang/Analysis/FlowSensitive/MatchSwitch.h"
@@ -69,7 +70,8 @@ struct UncheckedStatusOrAccessModelOptions {};
6970

7071
// Dataflow analysis that discovers unsafe uses of StatusOr values.
7172
class UncheckedStatusOrAccessModel
72-
: public DataflowAnalysis<UncheckedStatusOrAccessModel, NoopLattice> {
73+
: public DataflowAnalysis<UncheckedStatusOrAccessModel,
74+
CachedConstAccessorsLattice<NoopLattice>> {
7375
public:
7476
explicit UncheckedStatusOrAccessModel(ASTContext &Ctx, Environment &Env);
7577

clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
2626
#include "clang/Analysis/FlowSensitive/MatchSwitch.h"
2727
#include "clang/Analysis/FlowSensitive/RecordOps.h"
28+
#include "clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h"
2829
#include "clang/Analysis/FlowSensitive/StorageLocation.h"
2930
#include "clang/Analysis/FlowSensitive/Value.h"
3031
#include "clang/Basic/LLVM.h"
@@ -237,6 +238,49 @@ static auto isAsStatusCallWithStatusOr() {
237238
hasArgument(0, hasType(statusOrType())));
238239
}
239240

241+
static auto possiblyReferencedStatusOrType() {
242+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
243+
return anyOf(statusOrType(), referenceType(pointee(statusOrType())));
244+
}
245+
246+
static auto isConstStatusOrAccessorMemberCall() {
247+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
248+
return cxxMemberCallExpr(callee(
249+
cxxMethodDecl(parameterCountIs(0), isConst(),
250+
returns(qualType(possiblyReferencedStatusOrType())))));
251+
}
252+
253+
static auto isConstStatusOrAccessorMemberOperatorCall() {
254+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
255+
return cxxOperatorCallExpr(
256+
callee(cxxMethodDecl(parameterCountIs(0), isConst(),
257+
returns(possiblyReferencedStatusOrType()))));
258+
}
259+
260+
static auto isConstStatusOrPointerAccessorMemberCall() {
261+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
262+
return cxxMemberCallExpr(callee(cxxMethodDecl(
263+
parameterCountIs(0), isConst(),
264+
returns(pointerType(pointee(possiblyReferencedStatusOrType()))))));
265+
}
266+
267+
static auto isConstStatusOrPointerAccessorMemberOperatorCall() {
268+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
269+
return cxxOperatorCallExpr(callee(cxxMethodDecl(
270+
parameterCountIs(0), isConst(),
271+
returns(pointerType(pointee(possiblyReferencedStatusOrType()))))));
272+
}
273+
274+
static auto isNonConstMemberCall() {
275+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
276+
return cxxMemberCallExpr(callee(cxxMethodDecl(unless(isConst()))));
277+
}
278+
279+
static auto isNonConstMemberOperatorCall() {
280+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
281+
return cxxOperatorCallExpr(callee(cxxMethodDecl(unless(isConst()))));
282+
}
283+
240284
static auto
241285
buildDiagnoseMatchSwitch(const UncheckedStatusOrAccessModelOptions &Options) {
242286
return CFGMatchSwitchBuilder<const Environment,
@@ -697,6 +741,117 @@ static void transferPointerToBoolean(const ImplicitCastExpr *Expr,
697741
dyn_cast_or_null<BoolValue>(State.Env.getValue(*Expr->getSubExpr())))
698742
State.Env.setValue(*Expr, *SubExprVal);
699743
}
744+
static void handleConstStatusOrAccessorMemberCall(
745+
const CallExpr *Expr, RecordStorageLocation *RecordLoc,
746+
const MatchFinder::MatchResult &Result, LatticeTransferState &State) {
747+
assert(isStatusOrType(Expr->getType()));
748+
if (RecordLoc == nullptr)
749+
return;
750+
const FunctionDecl *DirectCallee = Expr->getDirectCallee();
751+
if (DirectCallee == nullptr)
752+
return;
753+
StorageLocation &Loc =
754+
State.Lattice.getOrCreateConstMethodReturnStorageLocation(
755+
*RecordLoc, DirectCallee, State.Env, [&](StorageLocation &Loc) {
756+
initializeStatusOr(cast<RecordStorageLocation>(Loc), State.Env);
757+
});
758+
if (Expr->isPRValue()) {
759+
auto &ResultLoc = State.Env.getResultObjectLocation(*Expr);
760+
copyRecord(cast<RecordStorageLocation>(Loc), ResultLoc, State.Env);
761+
} else {
762+
State.Env.setStorageLocation(*Expr, Loc);
763+
}
764+
}
765+
766+
static void handleConstStatusOrPointerAccessorMemberCall(
767+
const CallExpr *Expr, RecordStorageLocation *RecordLoc,
768+
const MatchFinder::MatchResult &Result, LatticeTransferState &State) {
769+
if (RecordLoc == nullptr)
770+
return;
771+
auto *Val = State.Lattice.getOrCreateConstMethodReturnValue(*RecordLoc, Expr,
772+
State.Env);
773+
State.Env.setValue(*Expr, *Val);
774+
}
775+
776+
static void
777+
transferConstStatusOrAccessorMemberCall(const CXXMemberCallExpr *Expr,
778+
const MatchFinder::MatchResult &Result,
779+
LatticeTransferState &State) {
780+
handleConstStatusOrAccessorMemberCall(
781+
Expr, getImplicitObjectLocation(*Expr, State.Env), Result, State);
782+
}
783+
784+
static void transferConstStatusOrAccessorMemberOperatorCall(
785+
const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &Result,
786+
LatticeTransferState &State) {
787+
auto *RecordLoc = cast_or_null<RecordStorageLocation>(
788+
State.Env.getStorageLocation(*Expr->getArg(0)));
789+
handleConstStatusOrAccessorMemberCall(Expr, RecordLoc, Result, State);
790+
}
791+
792+
static void transferConstStatusOrPointerAccessorMemberCall(
793+
const CXXMemberCallExpr *Expr, const MatchFinder::MatchResult &Result,
794+
LatticeTransferState &State) {
795+
handleConstStatusOrPointerAccessorMemberCall(
796+
Expr, getImplicitObjectLocation(*Expr, State.Env), Result, State);
797+
}
798+
799+
static void transferConstStatusOrPointerAccessorMemberOperatorCall(
800+
const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &Result,
801+
LatticeTransferState &State) {
802+
auto *RecordLoc = cast_or_null<RecordStorageLocation>(
803+
State.Env.getStorageLocation(*Expr->getArg(0)));
804+
handleConstStatusOrPointerAccessorMemberCall(Expr, RecordLoc, Result, State);
805+
}
806+
807+
static void transferStatusOrReturningCall(const CallExpr *Expr,
808+
LatticeTransferState &State) {
809+
RecordStorageLocation *StatusOrLoc =
810+
Expr->isPRValue() ? &State.Env.getResultObjectLocation(*Expr)
811+
: State.Env.get<RecordStorageLocation>(*Expr);
812+
if (StatusOrLoc != nullptr &&
813+
State.Env.getValue(locForOk(locForStatus(*StatusOrLoc))) == nullptr)
814+
initializeStatusOr(*StatusOrLoc, State.Env);
815+
}
816+
817+
static void handleNonConstMemberCall(const CallExpr *Expr,
818+
RecordStorageLocation *RecordLoc,
819+
const MatchFinder::MatchResult &Result,
820+
LatticeTransferState &State) {
821+
if (RecordLoc == nullptr)
822+
return;
823+
State.Lattice.clearConstMethodReturnValues(*RecordLoc);
824+
State.Lattice.clearConstMethodReturnStorageLocations(*RecordLoc);
825+
826+
if (isStatusOrType(Expr->getType()))
827+
transferStatusOrReturningCall(Expr, State);
828+
}
829+
830+
static void transferNonConstMemberCall(const CXXMemberCallExpr *Expr,
831+
const MatchFinder::MatchResult &Result,
832+
LatticeTransferState &State) {
833+
handleNonConstMemberCall(Expr, getImplicitObjectLocation(*Expr, State.Env),
834+
Result, State);
835+
}
836+
837+
static void
838+
transferNonConstMemberOperatorCall(const CXXOperatorCallExpr *Expr,
839+
const MatchFinder::MatchResult &Result,
840+
LatticeTransferState &State) {
841+
auto *RecordLoc = cast_or_null<RecordStorageLocation>(
842+
State.Env.getStorageLocation(*Expr->getArg(0)));
843+
handleNonConstMemberCall(Expr, RecordLoc, Result, State);
844+
}
845+
846+
static RecordStorageLocation *
847+
getSmartPtrLikeStorageLocation(const Expr &E, const Environment &Env) {
848+
if (!E.isPRValue())
849+
return dyn_cast_or_null<RecordStorageLocation>(Env.getStorageLocation(E));
850+
if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Env.getValue(E)))
851+
return dyn_cast_or_null<RecordStorageLocation>(
852+
&PointerVal->getPointeeLoc());
853+
return nullptr;
854+
}
700855

701856
CFGMatchSwitch<LatticeTransferState>
702857
buildTransferMatchSwitch(ASTContext &Ctx,
@@ -755,6 +910,60 @@ buildTransferMatchSwitch(ASTContext &Ctx,
755910
transferLoggingGetReferenceableValueCall)
756911
.CaseOfCFGStmt<CallExpr>(isLoggingCheckEqImpl(),
757912
transferLoggingCheckEqImpl)
913+
// This needs to go before the const accessor call matcher, because these
914+
// look like them, but we model `operator`* and `get` to return the same
915+
// object. Also, we model them for non-const cases.
916+
.CaseOfCFGStmt<CXXOperatorCallExpr>(
917+
isPointerLikeOperatorStar(),
918+
[](const CXXOperatorCallExpr *E,
919+
const MatchFinder::MatchResult &Result,
920+
LatticeTransferState &State) {
921+
transferSmartPointerLikeCachedDeref(
922+
E, getSmartPtrLikeStorageLocation(*E->getArg(0), State.Env),
923+
State, [](StorageLocation &Loc) {});
924+
})
925+
.CaseOfCFGStmt<CXXOperatorCallExpr>(
926+
isPointerLikeOperatorArrow(),
927+
[](const CXXOperatorCallExpr *E,
928+
const MatchFinder::MatchResult &Result,
929+
LatticeTransferState &State) {
930+
transferSmartPointerLikeCachedGet(
931+
E, getSmartPtrLikeStorageLocation(*E->getArg(0), State.Env),
932+
State, [](StorageLocation &Loc) {});
933+
})
934+
.CaseOfCFGStmt<CXXMemberCallExpr>(
935+
isSmartPointerLikeValueMethodCall(),
936+
[](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &Result,
937+
LatticeTransferState &State) {
938+
transferSmartPointerLikeCachedDeref(
939+
E, getImplicitObjectLocation(*E, State.Env), State,
940+
[](StorageLocation &Loc) {});
941+
})
942+
.CaseOfCFGStmt<CXXMemberCallExpr>(
943+
isSmartPointerLikeGetMethodCall(),
944+
[](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &Result,
945+
LatticeTransferState &State) {
946+
transferSmartPointerLikeCachedGet(
947+
E, getImplicitObjectLocation(*E, State.Env), State,
948+
[](StorageLocation &Loc) {});
949+
})
950+
// const accessor calls
951+
.CaseOfCFGStmt<CXXMemberCallExpr>(isConstStatusOrAccessorMemberCall(),
952+
transferConstStatusOrAccessorMemberCall)
953+
.CaseOfCFGStmt<CXXOperatorCallExpr>(
954+
isConstStatusOrAccessorMemberOperatorCall(),
955+
transferConstStatusOrAccessorMemberOperatorCall)
956+
.CaseOfCFGStmt<CXXMemberCallExpr>(
957+
isConstStatusOrPointerAccessorMemberCall(),
958+
transferConstStatusOrPointerAccessorMemberCall)
959+
.CaseOfCFGStmt<CXXOperatorCallExpr>(
960+
isConstStatusOrPointerAccessorMemberOperatorCall(),
961+
transferConstStatusOrPointerAccessorMemberOperatorCall)
962+
// non-const member calls that may modify the state of an object.
963+
.CaseOfCFGStmt<CXXMemberCallExpr>(isNonConstMemberCall(),
964+
transferNonConstMemberCall)
965+
.CaseOfCFGStmt<CXXOperatorCallExpr>(isNonConstMemberOperatorCall(),
966+
transferNonConstMemberOperatorCall)
758967
// N.B. These need to come after all other CXXConstructExpr.
759968
// These are there to make sure that every Status and StatusOr object
760969
// have their ok boolean initialized when constructed. If we were to

clang/unittests/Analysis/FlowSensitive/MockHeaders.cpp

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2232,6 +2232,95 @@ using testing::AssertionResult;
22322232
#endif // TESTING_DEFS_H
22332233
)cc";
22342234

2235+
constexpr const char StdUniquePtrHeader[] = R"cc(
2236+
namespace std {
2237+
2238+
template <typename T>
2239+
struct default_delete {};
2240+
2241+
template <typename T, typename D = default_delete<T>>
2242+
class unique_ptr {
2243+
public:
2244+
using element_type = T;
2245+
using deleter_type = D;
2246+
2247+
constexpr unique_ptr();
2248+
constexpr unique_ptr(nullptr_t) noexcept;
2249+
unique_ptr(unique_ptr&&);
2250+
explicit unique_ptr(T*);
2251+
template <typename U, typename E>
2252+
unique_ptr(unique_ptr<U, E>&&);
2253+
2254+
~unique_ptr();
2255+
2256+
unique_ptr& operator=(unique_ptr&&);
2257+
template <typename U, typename E>
2258+
unique_ptr& operator=(unique_ptr<U, E>&&);
2259+
unique_ptr& operator=(nullptr_t);
2260+
2261+
void reset(T* = nullptr) noexcept;
2262+
T* release();
2263+
T* get() const;
2264+
2265+
T& operator*() const;
2266+
T* operator->() const;
2267+
explicit operator bool() const noexcept;
2268+
};
2269+
2270+
template <typename T, typename D>
2271+
class unique_ptr<T[], D> {
2272+
public:
2273+
T* get() const;
2274+
T& operator[](size_t i);
2275+
const T& operator[](size_t i) const;
2276+
};
2277+
2278+
template <typename T, typename... Args>
2279+
unique_ptr<T> make_unique(Args&&...);
2280+
2281+
template <class T, class D>
2282+
void swap(unique_ptr<T, D>& x, unique_ptr<T, D>& y) noexcept;
2283+
2284+
template <class T1, class D1, class T2, class D2>
2285+
bool operator==(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
2286+
template <class T1, class D1, class T2, class D2>
2287+
bool operator!=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
2288+
template <class T1, class D1, class T2, class D2>
2289+
bool operator<(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
2290+
template <class T1, class D1, class T2, class D2>
2291+
bool operator<=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
2292+
template <class T1, class D1, class T2, class D2>
2293+
bool operator>(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
2294+
template <class T1, class D1, class T2, class D2>
2295+
bool operator>=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
2296+
2297+
template <class T, class D>
2298+
bool operator==(const unique_ptr<T, D>& x, nullptr_t) noexcept;
2299+
template <class T, class D>
2300+
bool operator==(nullptr_t, const unique_ptr<T, D>& y) noexcept;
2301+
template <class T, class D>
2302+
bool operator!=(const unique_ptr<T, D>& x, nullptr_t) noexcept;
2303+
template <class T, class D>
2304+
bool operator!=(nullptr_t, const unique_ptr<T, D>& y) noexcept;
2305+
template <class T, class D>
2306+
bool operator<(const unique_ptr<T, D>& x, nullptr_t);
2307+
template <class T, class D>
2308+
bool operator<(nullptr_t, const unique_ptr<T, D>& y);
2309+
template <class T, class D>
2310+
bool operator<=(const unique_ptr<T, D>& x, nullptr_t);
2311+
template <class T, class D>
2312+
bool operator<=(nullptr_t, const unique_ptr<T, D>& y);
2313+
template <class T, class D>
2314+
bool operator>(const unique_ptr<T, D>& x, nullptr_t);
2315+
template <class T, class D>
2316+
bool operator>(nullptr_t, const unique_ptr<T, D>& y);
2317+
template <class T, class D>
2318+
bool operator>=(const unique_ptr<T, D>& x, nullptr_t);
2319+
template <class T, class D>
2320+
bool operator>=(nullptr_t, const unique_ptr<T, D>& y);
2321+
}
2322+
)cc";
2323+
22352324
std::vector<std::pair<std::string, std::string>> getMockHeaders() {
22362325
std::vector<std::pair<std::string, std::string>> Headers;
22372326
Headers.emplace_back("cstddef.h", CStdDefHeader);
@@ -2249,6 +2338,7 @@ std::vector<std::pair<std::string, std::string>> getMockHeaders() {
22492338
Headers.emplace_back("statusor_defs.h", StatusOrDefsHeader);
22502339
Headers.emplace_back("absl_log.h", AbslLogHeader);
22512340
Headers.emplace_back("testing_defs.h", TestingDefsHeader);
2341+
Headers.emplace_back("std_unique_ptr.h", StdUniquePtrHeader);
22522342
return Headers;
22532343
}
22542344

0 commit comments

Comments
 (0)