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