1 //===-- UncheckedOptionalAccessModel.cpp ------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines a dataflow analysis that detects unsafe uses of optional 10 // values. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h" 15 #include "clang/AST/ASTContext.h" 16 #include "clang/AST/DeclCXX.h" 17 #include "clang/AST/Expr.h" 18 #include "clang/AST/ExprCXX.h" 19 #include "clang/AST/Stmt.h" 20 #include "clang/ASTMatchers/ASTMatchers.h" 21 #include "clang/Analysis/CFG.h" 22 #include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h" 23 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" 24 #include "clang/Analysis/FlowSensitive/NoopLattice.h" 25 #include "clang/Analysis/FlowSensitive/StorageLocation.h" 26 #include "clang/Analysis/FlowSensitive/Value.h" 27 #include "clang/Basic/SourceLocation.h" 28 #include "llvm/ADT/StringRef.h" 29 #include "llvm/Support/Casting.h" 30 #include "llvm/Support/ErrorHandling.h" 31 #include <cassert> 32 #include <memory> 33 #include <optional> 34 #include <utility> 35 #include <vector> 36 37 namespace clang { 38 namespace dataflow { 39 namespace { 40 41 using namespace ::clang::ast_matchers; 42 using LatticeTransferState = TransferState<NoopLattice>; 43 44 DeclarationMatcher optionalClass() { 45 return classTemplateSpecializationDecl( 46 anyOf(hasName("std::optional"), hasName("std::__optional_storage_base"), 47 hasName("__optional_destruct_base"), hasName("absl::optional"), 48 hasName("base::Optional")), 49 hasTemplateArgument(0, refersToType(type().bind("T")))); 50 } 51 52 auto optionalOrAliasType() { 53 return hasUnqualifiedDesugaredType( 54 recordType(hasDeclaration(optionalClass()))); 55 } 56 57 /// Matches any of the spellings of the optional types and sugar, aliases, etc. 58 auto hasOptionalType() { return hasType(optionalOrAliasType()); } 59 60 auto isOptionalMemberCallWithName( 61 llvm::StringRef MemberName, 62 const std::optional<StatementMatcher> &Ignorable = std::nullopt) { 63 auto Exception = unless(Ignorable ? expr(anyOf(*Ignorable, cxxThisExpr())) 64 : cxxThisExpr()); 65 return cxxMemberCallExpr( 66 on(expr(Exception)), 67 callee(cxxMethodDecl(hasName(MemberName), ofClass(optionalClass())))); 68 } 69 70 auto isOptionalOperatorCallWithName( 71 llvm::StringRef operator_name, 72 const std::optional<StatementMatcher> &Ignorable = std::nullopt) { 73 return cxxOperatorCallExpr( 74 hasOverloadedOperatorName(operator_name), 75 callee(cxxMethodDecl(ofClass(optionalClass()))), 76 Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr()); 77 } 78 79 auto isMakeOptionalCall() { 80 return callExpr( 81 callee(functionDecl(hasAnyName( 82 "std::make_optional", "base::make_optional", "absl::make_optional"))), 83 hasOptionalType()); 84 } 85 86 auto nulloptTypeDecl() { 87 return namedDecl( 88 hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t")); 89 } 90 91 auto hasNulloptType() { return hasType(nulloptTypeDecl()); } 92 93 // `optional` or `nullopt_t` 94 auto hasAnyOptionalType() { 95 return hasType(hasUnqualifiedDesugaredType( 96 recordType(hasDeclaration(anyOf(nulloptTypeDecl(), optionalClass()))))); 97 } 98 99 100 auto inPlaceClass() { 101 return recordDecl( 102 hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t")); 103 } 104 105 auto isOptionalNulloptConstructor() { 106 return cxxConstructExpr( 107 hasOptionalType(), 108 hasDeclaration(cxxConstructorDecl(parameterCountIs(1), 109 hasParameter(0, hasNulloptType())))); 110 } 111 112 auto isOptionalInPlaceConstructor() { 113 return cxxConstructExpr(hasOptionalType(), 114 hasArgument(0, hasType(inPlaceClass()))); 115 } 116 117 auto isOptionalValueOrConversionConstructor() { 118 return cxxConstructExpr( 119 hasOptionalType(), 120 unless(hasDeclaration( 121 cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))), 122 argumentCountIs(1), hasArgument(0, unless(hasNulloptType()))); 123 } 124 125 auto isOptionalValueOrConversionAssignment() { 126 return cxxOperatorCallExpr( 127 hasOverloadedOperatorName("="), 128 callee(cxxMethodDecl(ofClass(optionalClass()))), 129 unless(hasDeclaration(cxxMethodDecl( 130 anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))), 131 argumentCountIs(2), hasArgument(1, unless(hasNulloptType()))); 132 } 133 134 auto isNulloptConstructor() { 135 return cxxConstructExpr(hasNulloptType(), argumentCountIs(1), 136 hasArgument(0, hasNulloptType())); 137 } 138 139 auto isOptionalNulloptAssignment() { 140 return cxxOperatorCallExpr(hasOverloadedOperatorName("="), 141 callee(cxxMethodDecl(ofClass(optionalClass()))), 142 argumentCountIs(2), 143 hasArgument(1, hasNulloptType())); 144 } 145 146 auto isStdSwapCall() { 147 return callExpr(callee(functionDecl(hasName("std::swap"))), 148 argumentCountIs(2), hasArgument(0, hasOptionalType()), 149 hasArgument(1, hasOptionalType())); 150 } 151 152 constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall"; 153 154 auto isValueOrStringEmptyCall() { 155 // `opt.value_or("").empty()` 156 return cxxMemberCallExpr( 157 callee(cxxMethodDecl(hasName("empty"))), 158 onImplicitObjectArgument(ignoringImplicit( 159 cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 160 callee(cxxMethodDecl(hasName("value_or"), 161 ofClass(optionalClass()))), 162 hasArgument(0, stringLiteral(hasSize(0)))) 163 .bind(ValueOrCallID)))); 164 } 165 166 auto isValueOrNotEqX() { 167 auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) { 168 return hasOperands( 169 ignoringImplicit( 170 cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 171 callee(cxxMethodDecl(hasName("value_or"), 172 ofClass(optionalClass()))), 173 hasArgument(0, Arg)) 174 .bind(ValueOrCallID)), 175 ignoringImplicit(Arg)); 176 }; 177 178 // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd 179 // support this pattern for any expression, but the AST does not have a 180 // generic expression comparison facility, so we specialize to common cases 181 // seen in practice. FIXME: define a matcher that compares values across 182 // nodes, which would let us generalize this to any `X`. 183 return binaryOperation(hasOperatorName("!="), 184 anyOf(ComparesToSame(cxxNullPtrLiteralExpr()), 185 ComparesToSame(stringLiteral(hasSize(0))), 186 ComparesToSame(integerLiteral(equals(0))))); 187 } 188 189 auto isCallReturningOptional() { 190 return callExpr(hasType(qualType(anyOf( 191 optionalOrAliasType(), referenceType(pointee(optionalOrAliasType())))))); 192 } 193 194 template <typename L, typename R> 195 auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) { 196 return cxxOperatorCallExpr( 197 anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")), 198 argumentCountIs(2), hasArgument(0, lhs_arg_matcher), 199 hasArgument(1, rhs_arg_matcher)); 200 } 201 202 // Ensures that `Expr` is mapped to a `BoolValue` and returns it. 203 BoolValue &forceBoolValue(Environment &Env, const Expr &Expr) { 204 auto *Value = cast_or_null<BoolValue>(Env.getValue(Expr, SkipPast::None)); 205 if (Value != nullptr) 206 return *Value; 207 208 auto &Loc = Env.createStorageLocation(Expr); 209 Value = &Env.makeAtomicBoolValue(); 210 Env.setValue(Loc, *Value); 211 Env.setStorageLocation(Expr, Loc); 212 return *Value; 213 } 214 215 /// Sets `HasValueVal` as the symbolic value that represents the "has_value" 216 /// property of the optional value `OptionalVal`. 217 void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) { 218 OptionalVal.setProperty("has_value", HasValueVal); 219 } 220 221 /// Creates a symbolic value for an `optional` value using `HasValueVal` as the 222 /// symbolic value of its "has_value" property. 223 StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) { 224 auto OptionalVal = std::make_unique<StructValue>(); 225 setHasValue(*OptionalVal, HasValueVal); 226 return Env.takeOwnership(std::move(OptionalVal)); 227 } 228 229 /// Returns the symbolic value that represents the "has_value" property of the 230 /// optional value `OptionalVal`. Returns null if `OptionalVal` is null. 231 BoolValue *getHasValue(Environment &Env, Value *OptionalVal) { 232 if (OptionalVal != nullptr) { 233 auto *HasValueVal = 234 cast_or_null<BoolValue>(OptionalVal->getProperty("has_value")); 235 if (HasValueVal == nullptr) { 236 HasValueVal = &Env.makeAtomicBoolValue(); 237 OptionalVal->setProperty("has_value", *HasValueVal); 238 } 239 return HasValueVal; 240 } 241 return nullptr; 242 } 243 244 /// If `Type` is a reference type, returns the type of its pointee. Otherwise, 245 /// returns `Type` itself. 246 QualType stripReference(QualType Type) { 247 return Type->isReferenceType() ? Type->getPointeeType() : Type; 248 } 249 250 /// Returns true if and only if `Type` is an optional type. 251 bool isOptionalType(QualType Type) { 252 if (!Type->isRecordType()) 253 return false; 254 // FIXME: Optimize this by avoiding the `getQualifiedNameAsString` call. 255 auto TypeName = Type->getAsCXXRecordDecl()->getQualifiedNameAsString(); 256 return TypeName == "std::optional" || TypeName == "absl::optional" || 257 TypeName == "base::Optional"; 258 } 259 260 /// Returns the number of optional wrappers in `Type`. 261 /// 262 /// For example, if `Type` is `optional<optional<int>>`, the result of this 263 /// function will be 2. 264 int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) { 265 if (!isOptionalType(Type)) 266 return 0; 267 return 1 + countOptionalWrappers( 268 ASTCtx, 269 cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl()) 270 ->getTemplateArgs() 271 .get(0) 272 .getAsType() 273 .getDesugaredType(ASTCtx)); 274 } 275 276 /// Tries to initialize the `optional`'s value (that is, contents), and return 277 /// its location. Returns nullptr if the value can't be represented. 278 StorageLocation *maybeInitializeOptionalValueMember(QualType Q, 279 Value &OptionalVal, 280 Environment &Env) { 281 // The "value" property represents a synthetic field. As such, it needs 282 // `StorageLocation`, like normal fields (and other variables). So, we model 283 // it with a `ReferenceValue`, since that includes a storage location. Once 284 // the property is set, it will be shared by all environments that access the 285 // `Value` representing the optional (here, `OptionalVal`). 286 if (auto *ValueProp = OptionalVal.getProperty("value")) { 287 auto *ValueRef = clang::cast<ReferenceValue>(ValueProp); 288 auto &ValueLoc = ValueRef->getReferentLoc(); 289 if (Env.getValue(ValueLoc) == nullptr) { 290 // The property was previously set, but the value has been lost. This can 291 // happen, for example, because of an environment merge (where the two 292 // environments mapped the property to different values, which resulted in 293 // them both being discarded), or when two blocks in the CFG, with neither 294 // a dominator of the other, visit the same optional value, or even when a 295 // block is revisited during testing to collect per-statement state. 296 // FIXME: This situation means that the optional contents are not shared 297 // between branches and the like. Practically, this lack of sharing 298 // reduces the precision of the model when the contents are relevant to 299 // the check, like another optional or a boolean that influences control 300 // flow. 301 auto *ValueVal = Env.createValue(ValueLoc.getType()); 302 if (ValueVal == nullptr) 303 return nullptr; 304 Env.setValue(ValueLoc, *ValueVal); 305 } 306 return &ValueLoc; 307 } 308 309 auto Ty = stripReference(Q); 310 auto *ValueVal = Env.createValue(Ty); 311 if (ValueVal == nullptr) 312 return nullptr; 313 auto &ValueLoc = Env.createStorageLocation(Ty); 314 Env.setValue(ValueLoc, *ValueVal); 315 auto ValueRef = std::make_unique<ReferenceValue>(ValueLoc); 316 OptionalVal.setProperty("value", Env.takeOwnership(std::move(ValueRef))); 317 return &ValueLoc; 318 } 319 320 void initializeOptionalReference(const Expr *OptionalExpr, 321 const MatchFinder::MatchResult &, 322 LatticeTransferState &State) { 323 if (auto *OptionalVal = 324 State.Env.getValue(*OptionalExpr, SkipPast::Reference)) { 325 if (OptionalVal->getProperty("has_value") == nullptr) { 326 setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue()); 327 } 328 } 329 } 330 331 /// Returns true if and only if `OptionalVal` is initialized and known to be 332 /// empty in `Env. 333 bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) { 334 auto *HasValueVal = 335 cast_or_null<BoolValue>(OptionalVal.getProperty("has_value")); 336 return HasValueVal != nullptr && 337 Env.flowConditionImplies(Env.makeNot(*HasValueVal)); 338 } 339 340 /// Returns true if and only if `OptionalVal` is initialized and known to be 341 /// non-empty in `Env. 342 bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) { 343 auto *HasValueVal = 344 cast_or_null<BoolValue>(OptionalVal.getProperty("has_value")); 345 return HasValueVal != nullptr && Env.flowConditionImplies(*HasValueVal); 346 } 347 348 void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 349 LatticeTransferState &State) { 350 if (auto *OptionalVal = 351 State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) { 352 if (State.Env.getStorageLocation(*UnwrapExpr, SkipPast::None) == nullptr) 353 if (auto *Loc = maybeInitializeOptionalValueMember( 354 UnwrapExpr->getType(), *OptionalVal, State.Env)) 355 State.Env.setStorageLocation(*UnwrapExpr, *Loc); 356 } 357 } 358 359 void transferMakeOptionalCall(const CallExpr *E, 360 const MatchFinder::MatchResult &, 361 LatticeTransferState &State) { 362 auto &Loc = State.Env.createStorageLocation(*E); 363 State.Env.setStorageLocation(*E, Loc); 364 State.Env.setValue( 365 Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true))); 366 } 367 368 void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr, 369 const MatchFinder::MatchResult &, 370 LatticeTransferState &State) { 371 if (auto *HasValueVal = getHasValue( 372 State.Env, State.Env.getValue(*CallExpr->getImplicitObjectArgument(), 373 SkipPast::ReferenceThenPointer))) { 374 auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr); 375 State.Env.setValue(CallExprLoc, *HasValueVal); 376 State.Env.setStorageLocation(*CallExpr, CallExprLoc); 377 } 378 } 379 380 /// `ModelPred` builds a logical formula relating the predicate in 381 /// `ValueOrPredExpr` to the optional's `has_value` property. 382 void transferValueOrImpl(const clang::Expr *ValueOrPredExpr, 383 const MatchFinder::MatchResult &Result, 384 LatticeTransferState &State, 385 BoolValue &(*ModelPred)(Environment &Env, 386 BoolValue &ExprVal, 387 BoolValue &HasValueVal)) { 388 auto &Env = State.Env; 389 390 const auto *ObjectArgumentExpr = 391 Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID) 392 ->getImplicitObjectArgument(); 393 394 auto *HasValueVal = getHasValue( 395 State.Env, 396 State.Env.getValue(*ObjectArgumentExpr, SkipPast::ReferenceThenPointer)); 397 if (HasValueVal == nullptr) 398 return; 399 400 Env.addToFlowCondition( 401 ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr), *HasValueVal)); 402 } 403 404 void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr, 405 const MatchFinder::MatchResult &Result, 406 LatticeTransferState &State) { 407 return transferValueOrImpl(ComparisonExpr, Result, State, 408 [](Environment &Env, BoolValue &ExprVal, 409 BoolValue &HasValueVal) -> BoolValue & { 410 // If the result is *not* empty, then we know the 411 // optional must have been holding a value. If 412 // `ExprVal` is true, though, we don't learn 413 // anything definite about `has_value`, so we 414 // don't add any corresponding implications to 415 // the flow condition. 416 return Env.makeImplication(Env.makeNot(ExprVal), 417 HasValueVal); 418 }); 419 } 420 421 void transferValueOrNotEqX(const Expr *ComparisonExpr, 422 const MatchFinder::MatchResult &Result, 423 LatticeTransferState &State) { 424 transferValueOrImpl(ComparisonExpr, Result, State, 425 [](Environment &Env, BoolValue &ExprVal, 426 BoolValue &HasValueVal) -> BoolValue & { 427 // We know that if `(opt.value_or(X) != X)` then 428 // `opt.hasValue()`, even without knowing further 429 // details about the contents of `opt`. 430 return Env.makeImplication(ExprVal, HasValueVal); 431 }); 432 } 433 434 void transferCallReturningOptional(const CallExpr *E, 435 const MatchFinder::MatchResult &Result, 436 LatticeTransferState &State) { 437 if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr) 438 return; 439 440 auto &Loc = State.Env.createStorageLocation(*E); 441 State.Env.setStorageLocation(*E, Loc); 442 State.Env.setValue( 443 Loc, createOptionalValue(State.Env, State.Env.makeAtomicBoolValue())); 444 } 445 446 void assignOptionalValue(const Expr &E, Environment &Env, 447 BoolValue &HasValueVal) { 448 if (auto *OptionalLoc = 449 Env.getStorageLocation(E, SkipPast::ReferenceThenPointer)) { 450 Env.setValue(*OptionalLoc, createOptionalValue(Env, HasValueVal)); 451 } 452 } 453 454 /// Returns a symbolic value for the "has_value" property of an `optional<T>` 455 /// value that is constructed/assigned from a value of type `U` or `optional<U>` 456 /// where `T` is constructible from `U`. 457 BoolValue &valueOrConversionHasValue(const FunctionDecl &F, const Expr &E, 458 const MatchFinder::MatchResult &MatchRes, 459 LatticeTransferState &State) { 460 assert(F.getTemplateSpecializationArgs() != nullptr); 461 assert(F.getTemplateSpecializationArgs()->size() > 0); 462 463 const int TemplateParamOptionalWrappersCount = countOptionalWrappers( 464 *MatchRes.Context, 465 stripReference(F.getTemplateSpecializationArgs()->get(0).getAsType())); 466 const int ArgTypeOptionalWrappersCount = 467 countOptionalWrappers(*MatchRes.Context, stripReference(E.getType())); 468 469 // Check if this is a constructor/assignment call for `optional<T>` with 470 // argument of type `U` such that `T` is constructible from `U`. 471 if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount) 472 return State.Env.getBoolLiteralValue(true); 473 474 // This is a constructor/assignment call for `optional<T>` with argument of 475 // type `optional<U>` such that `T` is constructible from `U`. 476 if (auto *HasValueVal = 477 getHasValue(State.Env, State.Env.getValue(E, SkipPast::Reference))) 478 return *HasValueVal; 479 return State.Env.makeAtomicBoolValue(); 480 } 481 482 void transferValueOrConversionConstructor( 483 const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes, 484 LatticeTransferState &State) { 485 assert(E->getNumArgs() > 0); 486 487 assignOptionalValue(*E, State.Env, 488 valueOrConversionHasValue(*E->getConstructor(), 489 *E->getArg(0), MatchRes, 490 State)); 491 } 492 493 void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal, 494 LatticeTransferState &State) { 495 assert(E->getNumArgs() > 0); 496 497 auto *OptionalLoc = 498 State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 499 if (OptionalLoc == nullptr) 500 return; 501 502 State.Env.setValue(*OptionalLoc, createOptionalValue(State.Env, HasValueVal)); 503 504 // Assign a storage location for the whole expression. 505 State.Env.setStorageLocation(*E, *OptionalLoc); 506 } 507 508 void transferValueOrConversionAssignment( 509 const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes, 510 LatticeTransferState &State) { 511 assert(E->getNumArgs() > 1); 512 transferAssignment(E, 513 valueOrConversionHasValue(*E->getDirectCallee(), 514 *E->getArg(1), MatchRes, State), 515 State); 516 } 517 518 void transferNulloptAssignment(const CXXOperatorCallExpr *E, 519 const MatchFinder::MatchResult &, 520 LatticeTransferState &State) { 521 transferAssignment(E, State.Env.getBoolLiteralValue(false), State); 522 } 523 524 void transferSwap(const StorageLocation &OptionalLoc1, 525 const StorageLocation &OptionalLoc2, 526 LatticeTransferState &State) { 527 auto *OptionalVal1 = State.Env.getValue(OptionalLoc1); 528 assert(OptionalVal1 != nullptr); 529 530 auto *OptionalVal2 = State.Env.getValue(OptionalLoc2); 531 assert(OptionalVal2 != nullptr); 532 533 State.Env.setValue(OptionalLoc1, *OptionalVal2); 534 State.Env.setValue(OptionalLoc2, *OptionalVal1); 535 } 536 537 void transferSwapCall(const CXXMemberCallExpr *E, 538 const MatchFinder::MatchResult &, 539 LatticeTransferState &State) { 540 assert(E->getNumArgs() == 1); 541 542 auto *OptionalLoc1 = State.Env.getStorageLocation( 543 *E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer); 544 assert(OptionalLoc1 != nullptr); 545 546 auto *OptionalLoc2 = 547 State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 548 assert(OptionalLoc2 != nullptr); 549 550 transferSwap(*OptionalLoc1, *OptionalLoc2, State); 551 } 552 553 void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &, 554 LatticeTransferState &State) { 555 assert(E->getNumArgs() == 2); 556 557 auto *OptionalLoc1 = 558 State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 559 assert(OptionalLoc1 != nullptr); 560 561 auto *OptionalLoc2 = 562 State.Env.getStorageLocation(*E->getArg(1), SkipPast::Reference); 563 assert(OptionalLoc2 != nullptr); 564 565 transferSwap(*OptionalLoc1, *OptionalLoc2, State); 566 } 567 568 BoolValue &evaluateEquality(Environment &Env, BoolValue &EqVal, BoolValue &LHS, 569 BoolValue &RHS) { 570 // Logically, an optional<T> object is composed of two values - a `has_value` 571 // bit and a value of type T. Equality of optional objects compares both 572 // values. Therefore, merely comparing the `has_value` bits isn't sufficient: 573 // when two optional objects are engaged, the equality of their respective 574 // values of type T matters. Since we only track the `has_value` bits, we 575 // can't make any conclusions about equality when we know that two optional 576 // objects are engaged. 577 // 578 // We express this as two facts about the equality: 579 // a) EqVal => (LHS & RHS) v (!RHS & !LHS) 580 // If they are equal, then either both are set or both are unset. 581 // b) (!LHS & !RHS) => EqVal 582 // If neither is set, then they are equal. 583 // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula. 584 return Env.makeAnd( 585 Env.makeImplication( 586 EqVal, Env.makeOr(Env.makeAnd(LHS, RHS), 587 Env.makeAnd(Env.makeNot(LHS), Env.makeNot(RHS)))), 588 Env.makeImplication(Env.makeNot(EqVal), Env.makeOr(LHS, RHS))); 589 } 590 591 void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr, 592 const MatchFinder::MatchResult &, 593 LatticeTransferState &State) { 594 Environment &Env = State.Env; 595 auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 596 if (auto *LHasVal = getHasValue( 597 Env, Env.getValue(*CmpExpr->getArg(0), SkipPast::Reference))) 598 if (auto *RHasVal = getHasValue( 599 Env, Env.getValue(*CmpExpr->getArg(1), SkipPast::Reference))) { 600 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 601 CmpValue = &State.Env.makeNot(*CmpValue); 602 Env.addToFlowCondition( 603 evaluateEquality(Env, *CmpValue, *LHasVal, *RHasVal)); 604 } 605 } 606 607 void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr, 608 const clang::Expr *E, Environment &Env) { 609 auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 610 if (auto *HasVal = getHasValue(Env, Env.getValue(*E, SkipPast::Reference))) { 611 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 612 CmpValue = &Env.makeNot(*CmpValue); 613 Env.addToFlowCondition(evaluateEquality(Env, *CmpValue, *HasVal, 614 Env.getBoolLiteralValue(true))); 615 } 616 } 617 618 std::optional<StatementMatcher> 619 ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) { 620 if (Options.IgnoreSmartPointerDereference) { 621 auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr( 622 anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")), 623 unless(hasArgument(0, expr(hasOptionalType())))))); 624 return expr( 625 anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse)))); 626 } 627 return std::nullopt; 628 } 629 630 StatementMatcher 631 valueCall(const std::optional<StatementMatcher> &IgnorableOptional) { 632 return isOptionalMemberCallWithName("value", IgnorableOptional); 633 } 634 635 StatementMatcher 636 valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) { 637 return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional), 638 isOptionalOperatorCallWithName("->", IgnorableOptional))); 639 } 640 641 auto buildTransferMatchSwitch() { 642 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 643 // lot of duplicated work (e.g. string comparisons), consider providing APIs 644 // that avoid it through memoization. 645 return CFGMatchSwitchBuilder<LatticeTransferState>() 646 // Attach a symbolic "has_value" state to optional values that we see for 647 // the first time. 648 .CaseOfCFGStmt<Expr>( 649 expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()), 650 initializeOptionalReference) 651 652 // make_optional 653 .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall) 654 655 // optional::optional (in place) 656 .CaseOfCFGStmt<CXXConstructExpr>( 657 isOptionalInPlaceConstructor(), 658 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 659 LatticeTransferState &State) { 660 assignOptionalValue(*E, State.Env, 661 State.Env.getBoolLiteralValue(true)); 662 }) 663 // nullopt_t::nullopt_t 664 .CaseOfCFGStmt<CXXConstructExpr>( 665 isNulloptConstructor(), 666 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 667 LatticeTransferState &State) { 668 assignOptionalValue(*E, State.Env, 669 State.Env.getBoolLiteralValue(false)); 670 }) 671 // optional::optional(nullopt_t) 672 .CaseOfCFGStmt<CXXConstructExpr>( 673 isOptionalNulloptConstructor(), 674 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 675 LatticeTransferState &State) { 676 assignOptionalValue(*E, State.Env, 677 State.Env.getBoolLiteralValue(false)); 678 }) 679 // optional::optional (value/conversion) 680 .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(), 681 transferValueOrConversionConstructor) 682 683 684 // optional::operator= 685 .CaseOfCFGStmt<CXXOperatorCallExpr>( 686 isOptionalValueOrConversionAssignment(), 687 transferValueOrConversionAssignment) 688 .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(), 689 transferNulloptAssignment) 690 691 // optional::value 692 .CaseOfCFGStmt<CXXMemberCallExpr>( 693 valueCall(std::nullopt), 694 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 695 LatticeTransferState &State) { 696 transferUnwrapCall(E, E->getImplicitObjectArgument(), State); 697 }) 698 699 // optional::operator*, optional::operator-> 700 .CaseOfCFGStmt<CallExpr>(valueOperatorCall(std::nullopt), 701 [](const CallExpr *E, 702 const MatchFinder::MatchResult &, 703 LatticeTransferState &State) { 704 transferUnwrapCall(E, E->getArg(0), State); 705 }) 706 707 // optional::has_value 708 .CaseOfCFGStmt<CXXMemberCallExpr>( 709 isOptionalMemberCallWithName("has_value"), 710 transferOptionalHasValueCall) 711 712 // optional::operator bool 713 .CaseOfCFGStmt<CXXMemberCallExpr>( 714 isOptionalMemberCallWithName("operator bool"), 715 transferOptionalHasValueCall) 716 717 // optional::emplace 718 .CaseOfCFGStmt<CXXMemberCallExpr>( 719 isOptionalMemberCallWithName("emplace"), 720 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 721 LatticeTransferState &State) { 722 assignOptionalValue(*E->getImplicitObjectArgument(), State.Env, 723 State.Env.getBoolLiteralValue(true)); 724 }) 725 726 // optional::reset 727 .CaseOfCFGStmt<CXXMemberCallExpr>( 728 isOptionalMemberCallWithName("reset"), 729 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 730 LatticeTransferState &State) { 731 assignOptionalValue(*E->getImplicitObjectArgument(), State.Env, 732 State.Env.getBoolLiteralValue(false)); 733 }) 734 735 // optional::swap 736 .CaseOfCFGStmt<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"), 737 transferSwapCall) 738 739 // std::swap 740 .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall) 741 742 // opt.value_or("").empty() 743 .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(), 744 transferValueOrStringEmptyCall) 745 746 // opt.value_or(X) != X 747 .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX) 748 749 // Comparisons (==, !=): 750 .CaseOfCFGStmt<CXXOperatorCallExpr>( 751 isComparisonOperatorCall(hasAnyOptionalType(), hasAnyOptionalType()), 752 transferOptionalAndOptionalCmp) 753 .CaseOfCFGStmt<CXXOperatorCallExpr>( 754 isComparisonOperatorCall(hasOptionalType(), 755 unless(hasAnyOptionalType())), 756 [](const clang::CXXOperatorCallExpr *Cmp, 757 const MatchFinder::MatchResult &, LatticeTransferState &State) { 758 transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env); 759 }) 760 .CaseOfCFGStmt<CXXOperatorCallExpr>( 761 isComparisonOperatorCall(unless(hasAnyOptionalType()), 762 hasOptionalType()), 763 [](const clang::CXXOperatorCallExpr *Cmp, 764 const MatchFinder::MatchResult &, LatticeTransferState &State) { 765 transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env); 766 }) 767 768 // returns optional 769 .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(), 770 transferCallReturningOptional) 771 772 .Build(); 773 } 774 775 std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *UnwrapExpr, 776 const Expr *ObjectExpr, 777 const Environment &Env) { 778 if (auto *OptionalVal = 779 Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) { 780 auto *Prop = OptionalVal->getProperty("has_value"); 781 if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) { 782 if (Env.flowConditionImplies(*HasValueVal)) 783 return {}; 784 } 785 } 786 787 // Record that this unwrap is *not* provably safe. 788 // FIXME: include either the name of the optional (if applicable) or a source 789 // range of the access for easier interpretation of the result. 790 return {ObjectExpr->getBeginLoc()}; 791 } 792 793 auto buildDiagnoseMatchSwitch( 794 const UncheckedOptionalAccessModelOptions &Options) { 795 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 796 // lot of duplicated work (e.g. string comparisons), consider providing APIs 797 // that avoid it through memoization. 798 auto IgnorableOptional = ignorableOptional(Options); 799 return CFGMatchSwitchBuilder<const Environment, std::vector<SourceLocation>>() 800 // optional::value 801 .CaseOfCFGStmt<CXXMemberCallExpr>( 802 valueCall(IgnorableOptional), 803 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 804 const Environment &Env) { 805 return diagnoseUnwrapCall(E, E->getImplicitObjectArgument(), Env); 806 }) 807 808 // optional::operator*, optional::operator-> 809 .CaseOfCFGStmt<CallExpr>( 810 valueOperatorCall(IgnorableOptional), 811 [](const CallExpr *E, const MatchFinder::MatchResult &, 812 const Environment &Env) { 813 return diagnoseUnwrapCall(E, E->getArg(0), Env); 814 }) 815 .Build(); 816 } 817 818 } // namespace 819 820 ast_matchers::DeclarationMatcher 821 UncheckedOptionalAccessModel::optionalClassDecl() { 822 return optionalClass(); 823 } 824 825 UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx) 826 : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx), 827 TransferMatchSwitch(buildTransferMatchSwitch()) {} 828 829 void UncheckedOptionalAccessModel::transfer(const CFGElement *Elt, 830 NoopLattice &L, Environment &Env) { 831 LatticeTransferState State(L, Env); 832 TransferMatchSwitch(*Elt, getASTContext(), State); 833 } 834 835 ComparisonResult UncheckedOptionalAccessModel::compare( 836 QualType Type, const Value &Val1, const Environment &Env1, 837 const Value &Val2, const Environment &Env2) { 838 if (!isOptionalType(Type)) 839 return ComparisonResult::Unknown; 840 bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1); 841 bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2); 842 if (MustNonEmpty1 && MustNonEmpty2) return ComparisonResult::Same; 843 // If exactly one is true, then they're different, no reason to check whether 844 // they're definitely empty. 845 if (MustNonEmpty1 || MustNonEmpty2) return ComparisonResult::Different; 846 // Check if they're both definitely empty. 847 return (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2)) 848 ? ComparisonResult::Same 849 : ComparisonResult::Different; 850 } 851 852 bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1, 853 const Environment &Env1, 854 const Value &Val2, 855 const Environment &Env2, 856 Value &MergedVal, 857 Environment &MergedEnv) { 858 if (!isOptionalType(Type)) 859 return true; 860 // FIXME: uses same approach as join for `BoolValues`. Requires non-const 861 // values, though, so will require updating the interface. 862 auto &HasValueVal = MergedEnv.makeAtomicBoolValue(); 863 bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1); 864 bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2); 865 if (MustNonEmpty1 && MustNonEmpty2) 866 MergedEnv.addToFlowCondition(HasValueVal); 867 else if ( 868 // Only make the costly calls to `isEmptyOptional` if we got "unknown" 869 // (false) for both calls to `isNonEmptyOptional`. 870 !MustNonEmpty1 && !MustNonEmpty2 && isEmptyOptional(Val1, Env1) && 871 isEmptyOptional(Val2, Env2)) 872 MergedEnv.addToFlowCondition(MergedEnv.makeNot(HasValueVal)); 873 setHasValue(MergedVal, HasValueVal); 874 return true; 875 } 876 877 Value *UncheckedOptionalAccessModel::widen(QualType Type, Value &Prev, 878 const Environment &PrevEnv, 879 Value &Current, 880 Environment &CurrentEnv) { 881 switch (compare(Type, Prev, PrevEnv, Current, CurrentEnv)) { 882 case ComparisonResult::Same: 883 return &Prev; 884 case ComparisonResult::Different: 885 if (auto *PrevHasVal = 886 cast_or_null<BoolValue>(Prev.getProperty("has_value"))) { 887 if (isa<TopBoolValue>(PrevHasVal)) 888 return &Prev; 889 } 890 if (auto *CurrentHasVal = 891 cast_or_null<BoolValue>(Current.getProperty("has_value"))) { 892 if (isa<TopBoolValue>(CurrentHasVal)) 893 return &Current; 894 } 895 return &createOptionalValue(CurrentEnv, CurrentEnv.makeTopBoolValue()); 896 case ComparisonResult::Unknown: 897 return nullptr; 898 } 899 llvm_unreachable("all cases covered in switch"); 900 } 901 902 UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser( 903 UncheckedOptionalAccessModelOptions Options) 904 : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {} 905 906 std::vector<SourceLocation> UncheckedOptionalAccessDiagnoser::diagnose( 907 ASTContext &Ctx, const CFGElement *Elt, const Environment &Env) { 908 return DiagnoseMatchSwitch(*Elt, Ctx, Env); 909 } 910 911 } // namespace dataflow 912 } // namespace clang 913