Skip to content

Commit 83f751a

Browse files
authored
[FlowSensitive] [StatusOr] [6/N] support pointer comparison (#164856)
1 parent 10bec2c commit 83f751a

File tree

2 files changed

+132
-0
lines changed

2 files changed

+132
-0
lines changed

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

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,15 @@ static auto isNotOkStatusCall() {
168168
"::absl::UnimplementedError", "::absl::UnknownError"))));
169169
}
170170

171+
static auto isPointerComparisonOperatorCall(std::string operator_name) {
172+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
173+
return binaryOperator(hasOperatorName(operator_name),
174+
hasLHS(hasType(hasCanonicalType(pointerType(
175+
pointee(anyOf(statusOrType(), statusType())))))),
176+
hasRHS(hasType(hasCanonicalType(pointerType(
177+
pointee(anyOf(statusOrType(), statusType())))))));
178+
}
179+
171180
static auto
172181
buildDiagnoseMatchSwitch(const UncheckedStatusOrAccessModelOptions &Options) {
173182
return CFGMatchSwitchBuilder<const Environment,
@@ -438,6 +447,58 @@ static void transferComparisonOperator(const CXXOperatorCallExpr *Expr,
438447
State.Env.setValue(*Expr, *LhsAndRhsVal);
439448
}
440449

450+
static RecordStorageLocation *getPointeeLocation(const Expr &Expr,
451+
Environment &Env) {
452+
if (auto *PointerVal = Env.get<PointerValue>(Expr))
453+
return dyn_cast<RecordStorageLocation>(&PointerVal->getPointeeLoc());
454+
return nullptr;
455+
}
456+
457+
static BoolValue *evaluatePointerEquality(const Expr *LhsExpr,
458+
const Expr *RhsExpr,
459+
Environment &Env) {
460+
assert(LhsExpr->getType()->isPointerType());
461+
assert(RhsExpr->getType()->isPointerType());
462+
RecordStorageLocation *LhsStatusLoc = nullptr;
463+
RecordStorageLocation *RhsStatusLoc = nullptr;
464+
if (isStatusOrType(LhsExpr->getType()->getPointeeType()) &&
465+
isStatusOrType(RhsExpr->getType()->getPointeeType())) {
466+
auto *LhsStatusOrLoc = getPointeeLocation(*LhsExpr, Env);
467+
auto *RhsStatusOrLoc = getPointeeLocation(*RhsExpr, Env);
468+
if (LhsStatusOrLoc == nullptr || RhsStatusOrLoc == nullptr)
469+
return nullptr;
470+
LhsStatusLoc = &locForStatus(*LhsStatusOrLoc);
471+
RhsStatusLoc = &locForStatus(*RhsStatusOrLoc);
472+
} else if (isStatusType(LhsExpr->getType()->getPointeeType()) &&
473+
isStatusType(RhsExpr->getType()->getPointeeType())) {
474+
LhsStatusLoc = getPointeeLocation(*LhsExpr, Env);
475+
RhsStatusLoc = getPointeeLocation(*RhsExpr, Env);
476+
}
477+
if (LhsStatusLoc == nullptr || RhsStatusLoc == nullptr)
478+
return nullptr;
479+
auto &LhsOkVal = valForOk(*LhsStatusLoc, Env);
480+
auto &RhsOkVal = valForOk(*RhsStatusLoc, Env);
481+
auto &Res = Env.makeAtomicBoolValue();
482+
auto &A = Env.arena();
483+
Env.assume(A.makeImplies(
484+
Res.formula(), A.makeEquals(LhsOkVal.formula(), RhsOkVal.formula())));
485+
return &Res;
486+
}
487+
488+
static void transferPointerComparisonOperator(const BinaryOperator *Expr,
489+
LatticeTransferState &State,
490+
bool IsNegative) {
491+
auto *LhsAndRhsVal =
492+
evaluatePointerEquality(Expr->getLHS(), Expr->getRHS(), State.Env);
493+
if (LhsAndRhsVal == nullptr)
494+
return;
495+
496+
if (IsNegative)
497+
State.Env.setValue(*Expr, State.Env.makeNot(*LhsAndRhsVal));
498+
else
499+
State.Env.setValue(*Expr, *LhsAndRhsVal);
500+
}
501+
441502
static void transferOkStatusCall(const CallExpr *Expr,
442503
const MatchFinder::MatchResult &,
443504
LatticeTransferState &State) {
@@ -482,6 +543,20 @@ buildTransferMatchSwitch(ASTContext &Ctx,
482543
transferComparisonOperator(Expr, State,
483544
/*IsNegative=*/true);
484545
})
546+
.CaseOfCFGStmt<BinaryOperator>(
547+
isPointerComparisonOperatorCall("=="),
548+
[](const BinaryOperator *Expr, const MatchFinder::MatchResult &,
549+
LatticeTransferState &State) {
550+
transferPointerComparisonOperator(Expr, State,
551+
/*IsNegative=*/false);
552+
})
553+
.CaseOfCFGStmt<BinaryOperator>(
554+
isPointerComparisonOperatorCall("!="),
555+
[](const BinaryOperator *Expr, const MatchFinder::MatchResult &,
556+
LatticeTransferState &State) {
557+
transferPointerComparisonOperator(Expr, State,
558+
/*IsNegative=*/true);
559+
})
485560
.CaseOfCFGStmt<CallExpr>(isOkStatusCall(), transferOkStatusCall)
486561
.CaseOfCFGStmt<CallExpr>(isNotOkStatusCall(), transferNotOkStatusCall)
487562
.Build();

clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2871,6 +2871,63 @@ TEST_P(UncheckedStatusOrAccessModelTest, EqualityCheck) {
28712871
)cc");
28722872
}
28732873

2874+
TEST_P(UncheckedStatusOrAccessModelTest, PointerEqualityCheck) {
2875+
ExpectDiagnosticsFor(
2876+
R"cc(
2877+
#include "unchecked_statusor_access_test_defs.h"
2878+
2879+
void target(STATUSOR_INT* x, STATUSOR_INT* y) {
2880+
if (x->ok()) {
2881+
if (x == y)
2882+
y->value();
2883+
else
2884+
y->value(); // [[unsafe]]
2885+
}
2886+
}
2887+
)cc");
2888+
ExpectDiagnosticsFor(
2889+
R"cc(
2890+
#include "unchecked_statusor_access_test_defs.h"
2891+
2892+
void target(STATUSOR_INT* x, STATUSOR_INT* y) {
2893+
if (x->ok()) {
2894+
if (x != y)
2895+
y->value(); // [[unsafe]]
2896+
else
2897+
y->value();
2898+
}
2899+
}
2900+
)cc");
2901+
ExpectDiagnosticsFor(
2902+
R"cc(
2903+
#include "unchecked_statusor_access_test_defs.h"
2904+
2905+
void target(STATUS* x, STATUS* y) {
2906+
auto sor = Make<STATUSOR_INT>();
2907+
if (x->ok()) {
2908+
if (x == y && sor.status() == *y)
2909+
sor.value();
2910+
else
2911+
sor.value(); // [[unsafe]]
2912+
}
2913+
}
2914+
)cc");
2915+
ExpectDiagnosticsFor(
2916+
R"cc(
2917+
#include "unchecked_statusor_access_test_defs.h"
2918+
2919+
void target(STATUS* x, STATUS* y) {
2920+
auto sor = Make<STATUSOR_INT>();
2921+
if (x->ok()) {
2922+
if (x != y)
2923+
sor.value(); // [[unsafe]]
2924+
else if (sor.status() == *y)
2925+
sor.value();
2926+
}
2927+
}
2928+
)cc");
2929+
}
2930+
28742931
} // namespace
28752932

28762933
std::string

0 commit comments

Comments
 (0)