Skip to content

Commit 0bdf0df

Browse files
committed
[𝘀𝗽𝗿] changes to main this commit is based on
Created using spr 1.3.7 [skip ci]
1 parent 735b1ad commit 0bdf0df

File tree

3 files changed

+910
-1
lines changed

3 files changed

+910
-1
lines changed

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

Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,80 @@ static auto valueOperatorCall() {
137137
isStatusOrOperatorCallWithName("->")));
138138
}
139139

140+
static clang::ast_matchers::TypeMatcher statusType() {
141+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
142+
return hasCanonicalType(qualType(hasDeclaration(statusClass())));
143+
}
144+
145+
static auto isComparisonOperatorCall(llvm::StringRef operator_name) {
146+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
147+
return cxxOperatorCallExpr(
148+
hasOverloadedOperatorName(operator_name), argumentCountIs(2),
149+
hasArgument(0, anyOf(hasType(statusType()), hasType(statusOrType()))),
150+
hasArgument(1, anyOf(hasType(statusType()), hasType(statusOrType()))));
151+
}
152+
153+
static auto isOkStatusCall() {
154+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
155+
return callExpr(callee(functionDecl(hasName("::absl::OkStatus"))));
156+
}
157+
158+
static auto isNotOkStatusCall() {
159+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
160+
return callExpr(callee(functionDecl(hasAnyName(
161+
"::absl::AbortedError", "::absl::AlreadyExistsError",
162+
"::absl::CancelledError", "::absl::DataLossError",
163+
"::absl::DeadlineExceededError", "::absl::FailedPreconditionError",
164+
"::absl::InternalError", "::absl::InvalidArgumentError",
165+
"::absl::NotFoundError", "::absl::OutOfRangeError",
166+
"::absl::PermissionDeniedError", "::absl::ResourceExhaustedError",
167+
"::absl::UnauthenticatedError", "::absl::UnavailableError",
168+
"::absl::UnimplementedError", "::absl::UnknownError"))));
169+
}
170+
171+
static auto isPointerComparisonOperatorCall(std::string operator_name) {
172+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
173+
return binaryOperator(
174+
hasOperatorName(operator_name),
175+
hasLHS(
176+
anyOf(hasType(hasCanonicalType(pointerType(pointee(statusOrType())))),
177+
hasType(hasCanonicalType(pointerType(pointee(statusType())))))),
178+
hasRHS(anyOf(
179+
hasType(hasCanonicalType(pointerType(pointee(statusOrType())))),
180+
hasType(hasCanonicalType(pointerType(pointee(statusType())))))));
181+
}
182+
183+
static auto isStatusOrValueAssignmentCall() {
184+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
185+
return cxxOperatorCallExpr(
186+
hasOverloadedOperatorName("="),
187+
callee(cxxMethodDecl(ofClass(statusOrClass()))),
188+
hasArgument(1, anyOf(hasType(hasUnqualifiedDesugaredType(
189+
type(equalsBoundNode("T")))),
190+
nullPointerConstant())));
191+
}
192+
193+
static auto isStatusOrValueConstructor() {
194+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
195+
return cxxConstructExpr(
196+
hasType(statusOrType()),
197+
hasArgument(0,
198+
anyOf(hasType(hasCanonicalType(type(equalsBoundNode("T")))),
199+
nullPointerConstant(),
200+
hasType(namedDecl(hasAnyName("absl::in_place_t",
201+
"std::in_place_t"))))));
202+
}
203+
204+
static auto isStatusOrConstructor() {
205+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
206+
return cxxConstructExpr(hasType(statusOrType()));
207+
}
208+
209+
static auto isStatusConstructor() {
210+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
211+
return cxxConstructExpr(hasType(statusType()));
212+
}
213+
140214
static auto
141215
buildDiagnoseMatchSwitch(const UncheckedStatusOrAccessModelOptions &Options) {
142216
return CFGMatchSwitchBuilder<const Environment,
@@ -312,6 +386,222 @@ static void transferStatusUpdateCall(const CXXMemberCallExpr *Expr,
312386
State.Env.setValue(locForOk(*ThisLoc), NewVal);
313387
}
314388

389+
static BoolValue *evaluateStatusEquality(RecordStorageLocation &LhsStatusLoc,
390+
RecordStorageLocation &RhsStatusLoc,
391+
Environment &Env) {
392+
auto &A = Env.arena();
393+
// Logically, a Status object is composed of an error code that could take one
394+
// of multiple possible values, including the "ok" value. We track whether a
395+
// Status object has an "ok" value and represent this as an `ok` bit. Equality
396+
// of Status objects compares their error codes. Therefore, merely comparing
397+
// the `ok` bits isn't sufficient: when two Status objects are assigned non-ok
398+
// error codes the equality of their respective error codes matters. Since we
399+
// only track the `ok` bits, we can't make any conclusions about equality when
400+
// we know that two Status objects have non-ok values.
401+
402+
auto &LhsOkVal = valForOk(LhsStatusLoc, Env);
403+
auto &RhsOkVal = valForOk(RhsStatusLoc, Env);
404+
405+
auto &Res = Env.makeAtomicBoolValue();
406+
407+
// lhs && rhs => res (a.k.a. !res => !lhs || !rhs)
408+
Env.assume(A.makeImplies(A.makeAnd(LhsOkVal.formula(), RhsOkVal.formula()),
409+
Res.formula()));
410+
// res => (lhs == rhs)
411+
Env.assume(A.makeImplies(
412+
Res.formula(), A.makeEquals(LhsOkVal.formula(), RhsOkVal.formula())));
413+
414+
return &Res;
415+
}
416+
417+
static BoolValue *
418+
evaluateStatusOrEquality(RecordStorageLocation &LhsStatusOrLoc,
419+
RecordStorageLocation &RhsStatusOrLoc,
420+
Environment &Env) {
421+
auto &A = Env.arena();
422+
// Logically, a StatusOr<T> object is composed of two values - a Status and a
423+
// value of type T. Equality of StatusOr objects compares both values.
424+
// Therefore, merely comparing the `ok` bits of the Status values isn't
425+
// sufficient. When two StatusOr objects are engaged, the equality of their
426+
// respective values of type T matters. Similarly, when two StatusOr objects
427+
// have Status values that have non-ok error codes, the equality of the error
428+
// codes matters. Since we only track the `ok` bits of the Status values, we
429+
// can't make any conclusions about equality when we know that two StatusOr
430+
// objects are engaged or when their Status values contain non-ok error codes.
431+
auto &LhsOkVal = valForOk(locForStatus(LhsStatusOrLoc), Env);
432+
auto &RhsOkVal = valForOk(locForStatus(RhsStatusOrLoc), Env);
433+
auto &res = Env.makeAtomicBoolValue();
434+
435+
// res => (lhs == rhs)
436+
Env.assume(A.makeImplies(
437+
res.formula(), A.makeEquals(LhsOkVal.formula(), RhsOkVal.formula())));
438+
return &res;
439+
}
440+
441+
static BoolValue *evaluateEquality(const Expr *LhsExpr, const Expr *RhsExpr,
442+
Environment &Env) {
443+
// Check the type of both sides in case an operator== is added that admits
444+
// different types.
445+
if (isStatusOrType(LhsExpr->getType()) &&
446+
isStatusOrType(RhsExpr->getType())) {
447+
auto *LhsStatusOrLoc = Env.get<RecordStorageLocation>(*LhsExpr);
448+
if (LhsStatusOrLoc == nullptr)
449+
return nullptr;
450+
auto *RhsStatusOrLoc = Env.get<RecordStorageLocation>(*RhsExpr);
451+
if (RhsStatusOrLoc == nullptr)
452+
return nullptr;
453+
454+
return evaluateStatusOrEquality(*LhsStatusOrLoc, *RhsStatusOrLoc, Env);
455+
}
456+
if (isStatusType(LhsExpr->getType()) && isStatusType(RhsExpr->getType())) {
457+
auto *LhsStatusLoc = Env.get<RecordStorageLocation>(*LhsExpr);
458+
if (LhsStatusLoc == nullptr)
459+
return nullptr;
460+
461+
auto *RhsStatusLoc = Env.get<RecordStorageLocation>(*RhsExpr);
462+
if (RhsStatusLoc == nullptr)
463+
return nullptr;
464+
465+
return evaluateStatusEquality(*LhsStatusLoc, *RhsStatusLoc, Env);
466+
}
467+
return nullptr;
468+
}
469+
470+
static void transferComparisonOperator(const CXXOperatorCallExpr *Expr,
471+
LatticeTransferState &State,
472+
bool IsNegative) {
473+
auto *LhsAndRhsVal =
474+
evaluateEquality(Expr->getArg(0), Expr->getArg(1), State.Env);
475+
if (LhsAndRhsVal == nullptr)
476+
return;
477+
478+
if (IsNegative)
479+
State.Env.setValue(*Expr, State.Env.makeNot(*LhsAndRhsVal));
480+
else
481+
State.Env.setValue(*Expr, *LhsAndRhsVal);
482+
}
483+
484+
static RecordStorageLocation *getPointeeLocation(const Expr &Expr,
485+
Environment &Env) {
486+
if (auto *PointerVal = Env.get<PointerValue>(Expr))
487+
return dyn_cast<RecordStorageLocation>(&PointerVal->getPointeeLoc());
488+
return nullptr;
489+
}
490+
491+
static BoolValue *evaluatePointerEquality(const Expr *LhsExpr,
492+
const Expr *RhsExpr,
493+
Environment &Env) {
494+
assert(LhsExpr->getType()->isPointerType());
495+
assert(RhsExpr->getType()->isPointerType());
496+
RecordStorageLocation *LhsStatusLoc = nullptr;
497+
RecordStorageLocation *RhsStatusLoc = nullptr;
498+
if (isStatusOrType(LhsExpr->getType()->getPointeeType()) &&
499+
isStatusOrType(RhsExpr->getType()->getPointeeType())) {
500+
auto *LhsStatusOrLoc = getPointeeLocation(*LhsExpr, Env);
501+
auto *RhsStatusOrLoc = getPointeeLocation(*RhsExpr, Env);
502+
if (LhsStatusOrLoc == nullptr || RhsStatusOrLoc == nullptr)
503+
return nullptr;
504+
LhsStatusLoc = &locForStatus(*LhsStatusOrLoc);
505+
RhsStatusLoc = &locForStatus(*RhsStatusOrLoc);
506+
} else if (isStatusType(LhsExpr->getType()->getPointeeType()) &&
507+
isStatusType(RhsExpr->getType()->getPointeeType())) {
508+
LhsStatusLoc = getPointeeLocation(*LhsExpr, Env);
509+
RhsStatusLoc = getPointeeLocation(*RhsExpr, Env);
510+
}
511+
if (LhsStatusLoc == nullptr || RhsStatusLoc == nullptr)
512+
return nullptr;
513+
auto &LhsOkVal = valForOk(*LhsStatusLoc, Env);
514+
auto &RhsOkVal = valForOk(*RhsStatusLoc, Env);
515+
auto &Res = Env.makeAtomicBoolValue();
516+
auto &A = Env.arena();
517+
Env.assume(A.makeImplies(
518+
Res.formula(), A.makeEquals(LhsOkVal.formula(), RhsOkVal.formula())));
519+
return &Res;
520+
}
521+
522+
static void transferPointerComparisonOperator(const BinaryOperator *Expr,
523+
LatticeTransferState &State,
524+
bool IsNegative) {
525+
auto *LhsAndRhsVal =
526+
evaluatePointerEquality(Expr->getLHS(), Expr->getRHS(), State.Env);
527+
if (LhsAndRhsVal == nullptr)
528+
return;
529+
530+
if (IsNegative)
531+
State.Env.setValue(*Expr, State.Env.makeNot(*LhsAndRhsVal));
532+
else
533+
State.Env.setValue(*Expr, *LhsAndRhsVal);
534+
}
535+
536+
static void transferOkStatusCall(const CallExpr *Expr,
537+
const MatchFinder::MatchResult &,
538+
LatticeTransferState &State) {
539+
auto &OkVal =
540+
initializeStatus(State.Env.getResultObjectLocation(*Expr), State.Env);
541+
State.Env.assume(OkVal.formula());
542+
}
543+
544+
static void transferNotOkStatusCall(const CallExpr *Expr,
545+
const MatchFinder::MatchResult &,
546+
LatticeTransferState &State) {
547+
auto &OkVal =
548+
initializeStatus(State.Env.getResultObjectLocation(*Expr), State.Env);
549+
auto &A = State.Env.arena();
550+
State.Env.assume(A.makeNot(OkVal.formula()));
551+
}
552+
553+
static void transferEmplaceCall(const CXXMemberCallExpr *Expr,
554+
const MatchFinder::MatchResult &,
555+
LatticeTransferState &State) {
556+
RecordStorageLocation *StatusOrLoc =
557+
getImplicitObjectLocation(*Expr, State.Env);
558+
if (StatusOrLoc == nullptr)
559+
return;
560+
561+
auto &OkVal = valForOk(locForStatus(*StatusOrLoc), State.Env);
562+
State.Env.assume(OkVal.formula());
563+
}
564+
565+
static void transferValueAssignmentCall(const CXXOperatorCallExpr *Expr,
566+
const MatchFinder::MatchResult &,
567+
LatticeTransferState &State) {
568+
assert(Expr->getNumArgs() > 1);
569+
570+
auto *StatusOrLoc = State.Env.get<RecordStorageLocation>(*Expr->getArg(0));
571+
if (StatusOrLoc == nullptr)
572+
return;
573+
574+
auto &OkVal = initializeStatusOr(*StatusOrLoc, State.Env);
575+
State.Env.assume(OkVal.formula());
576+
}
577+
578+
static void transferValueConstructor(const CXXConstructExpr *Expr,
579+
const MatchFinder::MatchResult &,
580+
LatticeTransferState &State) {
581+
auto &OkVal =
582+
initializeStatusOr(State.Env.getResultObjectLocation(*Expr), State.Env);
583+
State.Env.assume(OkVal.formula());
584+
}
585+
586+
static void transferStatusOrConstructor(const CXXConstructExpr *Expr,
587+
const MatchFinder::MatchResult &,
588+
LatticeTransferState &State) {
589+
RecordStorageLocation &StatusOrLoc = State.Env.getResultObjectLocation(*Expr);
590+
RecordStorageLocation &StatusLoc = locForStatus(StatusOrLoc);
591+
592+
if (State.Env.getValue(locForOk(StatusLoc)) == nullptr)
593+
initializeStatusOr(StatusOrLoc, State.Env);
594+
}
595+
596+
static void transferStatusConstructor(const CXXConstructExpr *Expr,
597+
const MatchFinder::MatchResult &,
598+
LatticeTransferState &State) {
599+
RecordStorageLocation &StatusLoc = State.Env.getResultObjectLocation(*Expr);
600+
601+
if (State.Env.getValue(locForOk(StatusLoc)) == nullptr)
602+
initializeStatus(StatusLoc, State.Env);
603+
}
604+
315605
CFGMatchSwitch<LatticeTransferState>
316606
buildTransferMatchSwitch(ASTContext &Ctx,
317607
CFGMatchSwitchBuilder<LatticeTransferState> Builder) {
@@ -325,6 +615,52 @@ buildTransferMatchSwitch(ASTContext &Ctx,
325615
transferStatusOkCall)
326616
.CaseOfCFGStmt<CXXMemberCallExpr>(isStatusMemberCallWithName("Update"),
327617
transferStatusUpdateCall)
618+
.CaseOfCFGStmt<CXXOperatorCallExpr>(
619+
isComparisonOperatorCall("=="),
620+
[](const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &,
621+
LatticeTransferState &State) {
622+
transferComparisonOperator(Expr, State,
623+
/*IsNegative=*/false);
624+
})
625+
.CaseOfCFGStmt<CXXOperatorCallExpr>(
626+
isComparisonOperatorCall("!="),
627+
[](const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &,
628+
LatticeTransferState &State) {
629+
transferComparisonOperator(Expr, State,
630+
/*IsNegative=*/true);
631+
})
632+
.CaseOfCFGStmt<BinaryOperator>(
633+
isPointerComparisonOperatorCall("=="),
634+
[](const BinaryOperator *Expr, const MatchFinder::MatchResult &,
635+
LatticeTransferState &State) {
636+
transferPointerComparisonOperator(Expr, State,
637+
/*IsNegative=*/false);
638+
})
639+
.CaseOfCFGStmt<BinaryOperator>(
640+
isPointerComparisonOperatorCall("!="),
641+
[](const BinaryOperator *Expr, const MatchFinder::MatchResult &,
642+
LatticeTransferState &State) {
643+
transferPointerComparisonOperator(Expr, State,
644+
/*IsNegative=*/true);
645+
})
646+
.CaseOfCFGStmt<CallExpr>(isOkStatusCall(), transferOkStatusCall)
647+
.CaseOfCFGStmt<CallExpr>(isNotOkStatusCall(), transferNotOkStatusCall)
648+
.CaseOfCFGStmt<CXXMemberCallExpr>(isStatusOrMemberCallWithName("emplace"),
649+
transferEmplaceCall)
650+
.CaseOfCFGStmt<CXXOperatorCallExpr>(isStatusOrValueAssignmentCall(),
651+
transferValueAssignmentCall)
652+
.CaseOfCFGStmt<CXXConstructExpr>(isStatusOrValueConstructor(),
653+
transferValueConstructor)
654+
// N.B. These need to come after all other CXXConstructExpr.
655+
// These are there to make sure that every Status and StatusOr object
656+
// have their ok boolean initialized when constructed. If we were to
657+
// lazily initialize them when we first access them, we can produce
658+
// false positives if that first access is in a control flow statement.
659+
// You can comment out these two constructors and see tests fail.
660+
.CaseOfCFGStmt<CXXConstructExpr>(isStatusOrConstructor(),
661+
transferStatusOrConstructor)
662+
.CaseOfCFGStmt<CXXConstructExpr>(isStatusConstructor(),
663+
transferStatusConstructor)
328664
.Build();
329665
}
330666

clang/unittests/Analysis/FlowSensitive/MockHeaders.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1356,7 +1356,7 @@ bool operator==(const Status &lhs, const Status &rhs);
13561356
bool operator!=(const Status &lhs, const Status &rhs);
13571357
13581358
Status OkStatus();
1359-
Status InvalidArgumentError(char *);
1359+
Status InvalidArgumentError(const char *);
13601360
13611361
#endif // STATUS_H
13621362
)cc";

0 commit comments

Comments
 (0)