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 #include <vector> 38 39 namespace clang { 40 namespace dataflow { 41 42 static bool isTopLevelNamespaceWithName(const NamespaceDecl &NS, 43 llvm::StringRef Name) { 44 return NS.getDeclName().isIdentifier() && NS.getName() == Name && 45 NS.getParent() != nullptr && NS.getParent()->isTranslationUnit(); 46 } 47 48 static bool hasOptionalClassName(const CXXRecordDecl &RD) { 49 if (!RD.getDeclName().isIdentifier()) 50 return false; 51 52 if (RD.getName() == "optional") { 53 if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext())) 54 return N->isStdNamespace() || isTopLevelNamespaceWithName(*N, "absl"); 55 return false; 56 } 57 58 if (RD.getName() == "Optional") { 59 // Check whether namespace is "::base". 60 const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()); 61 return N != nullptr && isTopLevelNamespaceWithName(*N, "base"); 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 isOptionalMemberCallWithName( 91 llvm::StringRef MemberName, 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(hasName(MemberName)))); 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( 113 callee(functionDecl(hasAnyName( 114 "std::make_optional", "base::make_optional", "absl::make_optional"))), 115 hasOptionalType()); 116 } 117 118 auto nulloptTypeDecl() { 119 return namedDecl( 120 hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t")); 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( 133 hasAnyName("std::in_place_t", "absl::in_place_t", "base::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, SkipPast::None)); 241 if (Value != nullptr) 242 return Value->formula(); 243 244 auto &Loc = Env.createStorageLocation(Expr); 245 Value = &Env.makeAtomicBoolValue(); 246 Env.setValue(Loc, *Value); 247 Env.setStorageLocation(Expr, Loc); 248 return Value->formula(); 249 } 250 251 /// Sets `HasValueVal` as the symbolic value that represents the "has_value" 252 /// property of the optional value `OptionalVal`. 253 void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) { 254 OptionalVal.setProperty("has_value", HasValueVal); 255 } 256 257 /// Creates a symbolic value for an `optional` value at an existing storage 258 /// location. Uses `HasValueVal` as the symbolic value of the "has_value" 259 /// property. 260 StructValue &createOptionalValue(AggregateStorageLocation &Loc, 261 BoolValue &HasValueVal, Environment &Env) { 262 auto &OptionalVal = Env.create<StructValue>(Loc); 263 Env.setValue(Loc, OptionalVal); 264 setHasValue(OptionalVal, HasValueVal); 265 return OptionalVal; 266 } 267 268 /// Returns the symbolic value that represents the "has_value" property of the 269 /// optional value `OptionalVal`. Returns null if `OptionalVal` is null. 270 BoolValue *getHasValue(Environment &Env, Value *OptionalVal) { 271 if (OptionalVal != nullptr) { 272 auto *HasValueVal = 273 cast_or_null<BoolValue>(OptionalVal->getProperty("has_value")); 274 if (HasValueVal == nullptr) { 275 HasValueVal = &Env.makeAtomicBoolValue(); 276 OptionalVal->setProperty("has_value", *HasValueVal); 277 } 278 return HasValueVal; 279 } 280 return nullptr; 281 } 282 283 /// Returns true if and only if `Type` is an optional type. 284 bool isOptionalType(QualType Type) { 285 if (!Type->isRecordType()) 286 return false; 287 const CXXRecordDecl *D = Type->getAsCXXRecordDecl(); 288 return D != nullptr && hasOptionalClassName(*D); 289 } 290 291 /// Returns the number of optional wrappers in `Type`. 292 /// 293 /// For example, if `Type` is `optional<optional<int>>`, the result of this 294 /// function will be 2. 295 int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) { 296 if (!isOptionalType(Type)) 297 return 0; 298 return 1 + countOptionalWrappers( 299 ASTCtx, 300 cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl()) 301 ->getTemplateArgs() 302 .get(0) 303 .getAsType() 304 .getDesugaredType(ASTCtx)); 305 } 306 307 /// Tries to initialize the `optional`'s value (that is, contents), and return 308 /// its location. Returns nullptr if the value can't be represented. 309 StorageLocation *maybeInitializeOptionalValueMember(QualType Q, 310 Value &OptionalVal, 311 Environment &Env) { 312 // The "value" property represents a synthetic field. As such, it needs 313 // `StorageLocation`, like normal fields (and other variables). So, we model 314 // it with a `PointerValue`, since that includes a storage location. Once 315 // the property is set, it will be shared by all environments that access the 316 // `Value` representing the optional (here, `OptionalVal`). 317 if (auto *ValueProp = OptionalVal.getProperty("value")) { 318 auto *ValuePtr = clang::cast<PointerValue>(ValueProp); 319 auto &ValueLoc = ValuePtr->getPointeeLoc(); 320 if (Env.getValue(ValueLoc) != nullptr) 321 return &ValueLoc; 322 323 // The property was previously set, but the value has been lost. This can 324 // happen in various situations, for example: 325 // - Because of an environment merge (where the two environments mapped the 326 // property to different values, which resulted in them both being 327 // discarded). 328 // - When two blocks in the CFG, with neither a dominator of the other, 329 // visit the same optional value. (FIXME: This is something we can and 330 // should fix -- see also the lengthy FIXME below.) 331 // - Or even when a block is revisited during testing to collect 332 // per-statement state. 333 // FIXME: This situation means that the optional contents are not shared 334 // between branches and the like. Practically, this lack of sharing 335 // reduces the precision of the model when the contents are relevant to 336 // the check, like another optional or a boolean that influences control 337 // flow. 338 if (ValueLoc.getType()->isRecordType()) { 339 refreshStructValue(cast<AggregateStorageLocation>(ValueLoc), Env); 340 return &ValueLoc; 341 } else { 342 auto *ValueVal = Env.createValue(ValueLoc.getType()); 343 if (ValueVal == nullptr) 344 return nullptr; 345 Env.setValue(ValueLoc, *ValueVal); 346 return &ValueLoc; 347 } 348 } 349 350 auto Ty = Q.getNonReferenceType(); 351 auto &ValueLoc = Env.createObject(Ty); 352 auto &ValuePtr = Env.create<PointerValue>(ValueLoc); 353 // FIXME: 354 // The change we make to the `value` property below may become visible to 355 // other blocks that aren't successors of the current block and therefore 356 // don't see the change we made above mapping `ValueLoc` to `ValueVal`. For 357 // example: 358 // 359 // void target(optional<int> oo, bool b) { 360 // // `oo` is associated with a `StructValue` here, which we will call 361 // // `OptionalVal`. 362 // 363 // // The `has_value` property is set on `OptionalVal` (but not the 364 // // `value` property yet). 365 // if (!oo.has_value()) return; 366 // 367 // if (b) { 368 // // Let's assume we transfer the `if` branch first. 369 // // 370 // // This causes us to call `maybeInitializeOptionalValueMember()`, 371 // // which causes us to set the `value` property on `OptionalVal` 372 // // (which had not been set until this point). This `value` property 373 // // refers to a `PointerValue`, which in turn refers to a 374 // // StorageLocation` that is associated to an `IntegerValue`. 375 // oo.value(); 376 // } else { 377 // // Let's assume we transfer the `else` branch after the `if` branch. 378 // // 379 // // We see the `value` property that the `if` branch set on 380 // // `OptionalVal`, but in the environment for this block, the 381 // // `StorageLocation` in the `PointerValue` is not associated with any 382 // // `Value`. 383 // oo.value(); 384 // } 385 // } 386 // 387 // This situation is currently "saved" by the code above that checks whether 388 // the `value` property is already set, and if, the `ValueLoc` is not 389 // associated with a `ValueVal`, creates a new `ValueVal`. 390 // 391 // However, what we should really do is to make sure that the change to the 392 // `value` property does not "leak" to other blocks that are not successors 393 // of this block. To do this, instead of simply setting the `value` property 394 // on the existing `OptionalVal`, we should create a new `Value` for the 395 // optional, set the property on that, and associate the storage location that 396 // is currently associated with the existing `OptionalVal` with the newly 397 // created `Value` instead. 398 OptionalVal.setProperty("value", ValuePtr); 399 return &ValueLoc; 400 } 401 402 void initializeOptionalReference(const Expr *OptionalExpr, 403 const MatchFinder::MatchResult &, 404 LatticeTransferState &State) { 405 if (auto *OptionalVal = 406 State.Env.getValue(*OptionalExpr, SkipPast::Reference)) { 407 if (OptionalVal->getProperty("has_value") == nullptr) { 408 setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue()); 409 } 410 } 411 } 412 413 /// Returns true if and only if `OptionalVal` is initialized and known to be 414 /// empty in `Env`. 415 bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) { 416 auto *HasValueVal = 417 cast_or_null<BoolValue>(OptionalVal.getProperty("has_value")); 418 return HasValueVal != nullptr && 419 Env.flowConditionImplies(Env.arena().makeNot(HasValueVal->formula())); 420 } 421 422 /// Returns true if and only if `OptionalVal` is initialized and known to be 423 /// non-empty in `Env`. 424 bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) { 425 auto *HasValueVal = 426 cast_or_null<BoolValue>(OptionalVal.getProperty("has_value")); 427 return HasValueVal != nullptr && 428 Env.flowConditionImplies(HasValueVal->formula()); 429 } 430 431 Value *getValueBehindPossiblePointer(const Expr &E, const Environment &Env) { 432 Value *Val = Env.getValue(E, SkipPast::Reference); 433 if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Val)) 434 return Env.getValue(PointerVal->getPointeeLoc()); 435 return Val; 436 } 437 438 void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 439 LatticeTransferState &State) { 440 if (auto *OptionalVal = 441 getValueBehindPossiblePointer(*ObjectExpr, State.Env)) { 442 if (State.Env.getStorageLocation(*UnwrapExpr, SkipPast::None) == nullptr) 443 if (auto *Loc = maybeInitializeOptionalValueMember( 444 UnwrapExpr->getType(), *OptionalVal, State.Env)) 445 State.Env.setStorageLocation(*UnwrapExpr, *Loc); 446 } 447 } 448 449 void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 450 LatticeTransferState &State) { 451 if (auto *OptionalVal = 452 getValueBehindPossiblePointer(*ObjectExpr, State.Env)) { 453 if (auto *Loc = maybeInitializeOptionalValueMember( 454 UnwrapExpr->getType()->getPointeeType(), *OptionalVal, State.Env)) { 455 State.Env.setValueStrict(*UnwrapExpr, 456 State.Env.create<PointerValue>(*Loc)); 457 } 458 } 459 } 460 461 void transferMakeOptionalCall(const CallExpr *E, 462 const MatchFinder::MatchResult &, 463 LatticeTransferState &State) { 464 createOptionalValue(State.Env.getResultObjectLocation(*E), 465 State.Env.getBoolLiteralValue(true), State.Env); 466 } 467 468 void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr, 469 const MatchFinder::MatchResult &, 470 LatticeTransferState &State) { 471 if (auto *HasValueVal = getHasValue( 472 State.Env, getValueBehindPossiblePointer( 473 *CallExpr->getImplicitObjectArgument(), State.Env))) { 474 auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr); 475 State.Env.setValue(CallExprLoc, *HasValueVal); 476 State.Env.setStorageLocation(*CallExpr, CallExprLoc); 477 } 478 } 479 480 /// `ModelPred` builds a logical formula relating the predicate in 481 /// `ValueOrPredExpr` to the optional's `has_value` property. 482 void transferValueOrImpl( 483 const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result, 484 LatticeTransferState &State, 485 const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal, 486 const Formula &HasValueVal)) { 487 auto &Env = State.Env; 488 489 const auto *ObjectArgumentExpr = 490 Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID) 491 ->getImplicitObjectArgument(); 492 493 auto *HasValueVal = getHasValue( 494 State.Env, getValueBehindPossiblePointer(*ObjectArgumentExpr, State.Env)); 495 if (HasValueVal == nullptr) 496 return; 497 498 Env.addToFlowCondition(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr), 499 HasValueVal->formula())); 500 } 501 502 void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr, 503 const MatchFinder::MatchResult &Result, 504 LatticeTransferState &State) { 505 return transferValueOrImpl(ComparisonExpr, Result, State, 506 [](Environment &Env, const Formula &ExprVal, 507 const Formula &HasValueVal) -> const Formula & { 508 auto &A = Env.arena(); 509 // If the result is *not* empty, then we know the 510 // optional must have been holding a value. If 511 // `ExprVal` is true, though, we don't learn 512 // anything definite about `has_value`, so we 513 // don't add any corresponding implications to 514 // the flow condition. 515 return A.makeImplies(A.makeNot(ExprVal), 516 HasValueVal); 517 }); 518 } 519 520 void transferValueOrNotEqX(const Expr *ComparisonExpr, 521 const MatchFinder::MatchResult &Result, 522 LatticeTransferState &State) { 523 transferValueOrImpl(ComparisonExpr, Result, State, 524 [](Environment &Env, const Formula &ExprVal, 525 const Formula &HasValueVal) -> const Formula & { 526 auto &A = Env.arena(); 527 // We know that if `(opt.value_or(X) != X)` then 528 // `opt.hasValue()`, even without knowing further 529 // details about the contents of `opt`. 530 return A.makeImplies(ExprVal, HasValueVal); 531 }); 532 } 533 534 void transferCallReturningOptional(const CallExpr *E, 535 const MatchFinder::MatchResult &Result, 536 LatticeTransferState &State) { 537 if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr) 538 return; 539 540 AggregateStorageLocation *Loc = nullptr; 541 if (E->isPRValue()) { 542 Loc = &State.Env.getResultObjectLocation(*E); 543 } else { 544 Loc = &cast<AggregateStorageLocation>(State.Env.createStorageLocation(*E)); 545 State.Env.setStorageLocationStrict(*E, *Loc); 546 } 547 548 createOptionalValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env); 549 } 550 551 void constructOptionalValue(const Expr &E, Environment &Env, 552 BoolValue &HasValueVal) { 553 AggregateStorageLocation &Loc = Env.getResultObjectLocation(E); 554 Env.setValueStrict(E, createOptionalValue(Loc, HasValueVal, Env)); 555 } 556 557 /// Returns a symbolic value for the "has_value" property of an `optional<T>` 558 /// value that is constructed/assigned from a value of type `U` or `optional<U>` 559 /// where `T` is constructible from `U`. 560 BoolValue &valueOrConversionHasValue(const FunctionDecl &F, const Expr &E, 561 const MatchFinder::MatchResult &MatchRes, 562 LatticeTransferState &State) { 563 assert(F.getTemplateSpecializationArgs() != nullptr); 564 assert(F.getTemplateSpecializationArgs()->size() > 0); 565 566 const int TemplateParamOptionalWrappersCount = 567 countOptionalWrappers(*MatchRes.Context, F.getTemplateSpecializationArgs() 568 ->get(0) 569 .getAsType() 570 .getNonReferenceType()); 571 const int ArgTypeOptionalWrappersCount = countOptionalWrappers( 572 *MatchRes.Context, E.getType().getNonReferenceType()); 573 574 // Check if this is a constructor/assignment call for `optional<T>` with 575 // argument of type `U` such that `T` is constructible from `U`. 576 if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount) 577 return State.Env.getBoolLiteralValue(true); 578 579 // This is a constructor/assignment call for `optional<T>` with argument of 580 // type `optional<U>` such that `T` is constructible from `U`. 581 if (auto *HasValueVal = 582 getHasValue(State.Env, State.Env.getValue(E, SkipPast::Reference))) 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<AggregateStorageLocation>( 603 State.Env.getStorageLocationStrict(*E->getArg(0)))) { 604 createOptionalValue(*Loc, HasValueVal, State.Env); 605 606 // Assign a storage location for the whole expression. 607 State.Env.setStorageLocationStrict(*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(AggregateStorageLocation *Loc1, 628 AggregateStorageLocation *Loc2, 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<AggregateStorageLocation>( 666 State.Env.getStorageLocationStrict(*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<AggregateStorageLocation>( 674 State.Env.getStorageLocationStrict(*E->getArg(0))); 675 auto *Arg1Loc = cast_or_null<AggregateStorageLocation>( 676 State.Env.getStorageLocationStrict(*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.getStorageLocationStrict(*E->getArg(0))) 685 State.Env.setStorageLocationStrict(*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( 717 Env, Env.getValue(*CmpExpr->getArg(0), SkipPast::Reference))) 718 if (auto *RHasVal = getHasValue( 719 Env, Env.getValue(*CmpExpr->getArg(1), SkipPast::Reference))) { 720 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 721 CmpValue = &A.makeNot(*CmpValue); 722 Env.addToFlowCondition(evaluateEquality(A, *CmpValue, LHasVal->formula(), 723 RHasVal->formula())); 724 } 725 } 726 727 void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr, 728 const clang::Expr *E, Environment &Env) { 729 auto &A = Env.arena(); 730 auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 731 if (auto *HasVal = getHasValue(Env, Env.getValue(*E, SkipPast::Reference))) { 732 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 733 CmpValue = &A.makeNot(*CmpValue); 734 Env.addToFlowCondition( 735 evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true))); 736 } 737 } 738 739 std::optional<StatementMatcher> 740 ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) { 741 if (Options.IgnoreSmartPointerDereference) { 742 auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr( 743 anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")), 744 unless(hasArgument(0, expr(hasOptionalType())))))); 745 return expr( 746 anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse)))); 747 } 748 return std::nullopt; 749 } 750 751 StatementMatcher 752 valueCall(const std::optional<StatementMatcher> &IgnorableOptional) { 753 return isOptionalMemberCallWithName("value", IgnorableOptional); 754 } 755 756 StatementMatcher 757 valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) { 758 return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional), 759 isOptionalOperatorCallWithName("->", IgnorableOptional))); 760 } 761 762 auto buildTransferMatchSwitch() { 763 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 764 // lot of duplicated work (e.g. string comparisons), consider providing APIs 765 // that avoid it through memoization. 766 return CFGMatchSwitchBuilder<LatticeTransferState>() 767 // Attach a symbolic "has_value" state to optional values that we see for 768 // the first time. 769 .CaseOfCFGStmt<Expr>( 770 expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()), 771 initializeOptionalReference) 772 773 // make_optional 774 .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall) 775 776 // optional::optional (in place) 777 .CaseOfCFGStmt<CXXConstructExpr>( 778 isOptionalInPlaceConstructor(), 779 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 780 LatticeTransferState &State) { 781 constructOptionalValue(*E, State.Env, 782 State.Env.getBoolLiteralValue(true)); 783 }) 784 // nullopt_t::nullopt_t 785 .CaseOfCFGStmt<CXXConstructExpr>( 786 isNulloptConstructor(), 787 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 788 LatticeTransferState &State) { 789 constructOptionalValue(*E, State.Env, 790 State.Env.getBoolLiteralValue(false)); 791 }) 792 // optional::optional(nullopt_t) 793 .CaseOfCFGStmt<CXXConstructExpr>( 794 isOptionalNulloptConstructor(), 795 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 796 LatticeTransferState &State) { 797 constructOptionalValue(*E, State.Env, 798 State.Env.getBoolLiteralValue(false)); 799 }) 800 // optional::optional (value/conversion) 801 .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(), 802 transferValueOrConversionConstructor) 803 804 // optional::operator= 805 .CaseOfCFGStmt<CXXOperatorCallExpr>( 806 isOptionalValueOrConversionAssignment(), 807 transferValueOrConversionAssignment) 808 .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(), 809 transferNulloptAssignment) 810 811 // optional::value 812 .CaseOfCFGStmt<CXXMemberCallExpr>( 813 valueCall(std::nullopt), 814 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 815 LatticeTransferState &State) { 816 transferUnwrapCall(E, E->getImplicitObjectArgument(), State); 817 }) 818 819 // optional::operator* 820 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"), 821 [](const CallExpr *E, 822 const MatchFinder::MatchResult &, 823 LatticeTransferState &State) { 824 transferUnwrapCall(E, E->getArg(0), State); 825 }) 826 827 // optional::operator-> 828 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"), 829 [](const CallExpr *E, 830 const MatchFinder::MatchResult &, 831 LatticeTransferState &State) { 832 transferArrowOpCall(E, E->getArg(0), State); 833 }) 834 835 // optional::has_value 836 .CaseOfCFGStmt<CXXMemberCallExpr>( 837 isOptionalMemberCallWithName("has_value"), 838 transferOptionalHasValueCall) 839 840 // optional::operator bool 841 .CaseOfCFGStmt<CXXMemberCallExpr>( 842 isOptionalMemberCallWithName("operator bool"), 843 transferOptionalHasValueCall) 844 845 // optional::emplace 846 .CaseOfCFGStmt<CXXMemberCallExpr>( 847 isOptionalMemberCallWithName("emplace"), 848 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 849 LatticeTransferState &State) { 850 if (AggregateStorageLocation *Loc = 851 getImplicitObjectLocation(*E, State.Env)) { 852 createOptionalValue(*Loc, State.Env.getBoolLiteralValue(true), 853 State.Env); 854 } 855 }) 856 857 // optional::reset 858 .CaseOfCFGStmt<CXXMemberCallExpr>( 859 isOptionalMemberCallWithName("reset"), 860 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 861 LatticeTransferState &State) { 862 if (AggregateStorageLocation *Loc = 863 getImplicitObjectLocation(*E, State.Env)) { 864 createOptionalValue(*Loc, State.Env.getBoolLiteralValue(false), 865 State.Env); 866 } 867 }) 868 869 // optional::swap 870 .CaseOfCFGStmt<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"), 871 transferSwapCall) 872 873 // std::swap 874 .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall) 875 876 // std::forward 877 .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall) 878 879 // opt.value_or("").empty() 880 .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(), 881 transferValueOrStringEmptyCall) 882 883 // opt.value_or(X) != X 884 .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX) 885 886 // Comparisons (==, !=): 887 .CaseOfCFGStmt<CXXOperatorCallExpr>( 888 isComparisonOperatorCall(hasAnyOptionalType(), hasAnyOptionalType()), 889 transferOptionalAndOptionalCmp) 890 .CaseOfCFGStmt<CXXOperatorCallExpr>( 891 isComparisonOperatorCall(hasOptionalType(), 892 unless(hasAnyOptionalType())), 893 [](const clang::CXXOperatorCallExpr *Cmp, 894 const MatchFinder::MatchResult &, LatticeTransferState &State) { 895 transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env); 896 }) 897 .CaseOfCFGStmt<CXXOperatorCallExpr>( 898 isComparisonOperatorCall(unless(hasAnyOptionalType()), 899 hasOptionalType()), 900 [](const clang::CXXOperatorCallExpr *Cmp, 901 const MatchFinder::MatchResult &, LatticeTransferState &State) { 902 transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env); 903 }) 904 905 // returns optional 906 .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(), 907 transferCallReturningOptional) 908 909 .Build(); 910 } 911 912 std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr, 913 const Environment &Env) { 914 if (auto *OptionalVal = getValueBehindPossiblePointer(*ObjectExpr, Env)) { 915 auto *Prop = OptionalVal->getProperty("has_value"); 916 if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) { 917 if (Env.flowConditionImplies(HasValueVal->formula())) 918 return {}; 919 } 920 } 921 922 // Record that this unwrap is *not* provably safe. 923 // FIXME: include either the name of the optional (if applicable) or a source 924 // range of the access for easier interpretation of the result. 925 return {ObjectExpr->getBeginLoc()}; 926 } 927 928 auto buildDiagnoseMatchSwitch( 929 const UncheckedOptionalAccessModelOptions &Options) { 930 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 931 // lot of duplicated work (e.g. string comparisons), consider providing APIs 932 // that avoid it through memoization. 933 auto IgnorableOptional = ignorableOptional(Options); 934 return CFGMatchSwitchBuilder<const Environment, std::vector<SourceLocation>>() 935 // optional::value 936 .CaseOfCFGStmt<CXXMemberCallExpr>( 937 valueCall(IgnorableOptional), 938 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 939 const Environment &Env) { 940 return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env); 941 }) 942 943 // optional::operator*, optional::operator-> 944 .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional), 945 [](const CallExpr *E, 946 const MatchFinder::MatchResult &, 947 const Environment &Env) { 948 return diagnoseUnwrapCall(E->getArg(0), Env); 949 }) 950 .Build(); 951 } 952 953 } // namespace 954 955 ast_matchers::DeclarationMatcher 956 UncheckedOptionalAccessModel::optionalClassDecl() { 957 return optionalClass(); 958 } 959 960 UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx) 961 : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx), 962 TransferMatchSwitch(buildTransferMatchSwitch()) {} 963 964 void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt, 965 NoopLattice &L, Environment &Env) { 966 LatticeTransferState State(L, Env); 967 TransferMatchSwitch(Elt, getASTContext(), State); 968 } 969 970 ComparisonResult UncheckedOptionalAccessModel::compare( 971 QualType Type, const Value &Val1, const Environment &Env1, 972 const Value &Val2, const Environment &Env2) { 973 if (!isOptionalType(Type)) 974 return ComparisonResult::Unknown; 975 bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1); 976 bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2); 977 if (MustNonEmpty1 && MustNonEmpty2) 978 return ComparisonResult::Same; 979 // If exactly one is true, then they're different, no reason to check whether 980 // they're definitely empty. 981 if (MustNonEmpty1 || MustNonEmpty2) 982 return ComparisonResult::Different; 983 // Check if they're both definitely empty. 984 return (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2)) 985 ? ComparisonResult::Same 986 : ComparisonResult::Different; 987 } 988 989 bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1, 990 const Environment &Env1, 991 const Value &Val2, 992 const Environment &Env2, 993 Value &MergedVal, 994 Environment &MergedEnv) { 995 if (!isOptionalType(Type)) 996 return true; 997 // FIXME: uses same approach as join for `BoolValues`. Requires non-const 998 // values, though, so will require updating the interface. 999 auto &HasValueVal = MergedEnv.makeAtomicBoolValue(); 1000 bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1); 1001 bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2); 1002 if (MustNonEmpty1 && MustNonEmpty2) 1003 MergedEnv.addToFlowCondition(HasValueVal.formula()); 1004 else if ( 1005 // Only make the costly calls to `isEmptyOptional` if we got "unknown" 1006 // (false) for both calls to `isNonEmptyOptional`. 1007 !MustNonEmpty1 && !MustNonEmpty2 && isEmptyOptional(Val1, Env1) && 1008 isEmptyOptional(Val2, Env2)) 1009 MergedEnv.addToFlowCondition( 1010 MergedEnv.arena().makeNot(HasValueVal.formula())); 1011 setHasValue(MergedVal, HasValueVal); 1012 return true; 1013 } 1014 1015 Value *UncheckedOptionalAccessModel::widen(QualType Type, Value &Prev, 1016 const Environment &PrevEnv, 1017 Value &Current, 1018 Environment &CurrentEnv) { 1019 switch (compare(Type, Prev, PrevEnv, Current, CurrentEnv)) { 1020 case ComparisonResult::Same: 1021 return &Prev; 1022 case ComparisonResult::Different: 1023 if (auto *PrevHasVal = 1024 cast_or_null<BoolValue>(Prev.getProperty("has_value"))) { 1025 if (isa<TopBoolValue>(PrevHasVal)) 1026 return &Prev; 1027 } 1028 if (auto *CurrentHasVal = 1029 cast_or_null<BoolValue>(Current.getProperty("has_value"))) { 1030 if (isa<TopBoolValue>(CurrentHasVal)) 1031 return &Current; 1032 } 1033 return &createOptionalValue(cast<StructValue>(Current).getAggregateLoc(), 1034 CurrentEnv.makeTopBoolValue(), CurrentEnv); 1035 case ComparisonResult::Unknown: 1036 return nullptr; 1037 } 1038 llvm_unreachable("all cases covered in switch"); 1039 } 1040 1041 UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser( 1042 UncheckedOptionalAccessModelOptions Options) 1043 : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {} 1044 1045 std::vector<SourceLocation> UncheckedOptionalAccessDiagnoser::diagnose( 1046 ASTContext &Ctx, const CFGElement *Elt, const Environment &Env) { 1047 return DiagnoseMatchSwitch(*Elt, Ctx, Env); 1048 } 1049 1050 } // namespace dataflow 1051 } // namespace clang 1052