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