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 static const CXXRecordDecl *getOptionalBaseClass(const CXXRecordDecl *RD) { 68 if (RD == nullptr) 69 return nullptr; 70 if (hasOptionalClassName(*RD)) 71 return RD; 72 73 if (!RD->hasDefinition()) 74 return nullptr; 75 76 for (const CXXBaseSpecifier &Base : RD->bases()) 77 if (const CXXRecordDecl *BaseClass = 78 getOptionalBaseClass(Base.getType()->getAsCXXRecordDecl())) 79 return BaseClass; 80 81 return nullptr; 82 } 83 84 namespace { 85 86 using namespace ::clang::ast_matchers; 87 using LatticeTransferState = TransferState<NoopLattice>; 88 89 AST_MATCHER(CXXRecordDecl, optionalClass) { return hasOptionalClassName(Node); } 90 91 AST_MATCHER(CXXRecordDecl, optionalOrDerivedClass) { 92 return getOptionalBaseClass(&Node) != nullptr; 93 } 94 95 auto desugarsToOptionalType() { 96 return hasUnqualifiedDesugaredType( 97 recordType(hasDeclaration(cxxRecordDecl(optionalClass())))); 98 } 99 100 auto desugarsToOptionalOrDerivedType() { 101 return hasUnqualifiedDesugaredType( 102 recordType(hasDeclaration(cxxRecordDecl(optionalOrDerivedClass())))); 103 } 104 105 auto hasOptionalType() { return hasType(desugarsToOptionalType()); } 106 107 /// Matches any of the spellings of the optional types and sugar, aliases, 108 /// derived classes, etc. 109 auto hasOptionalOrDerivedType() { 110 return hasType(desugarsToOptionalOrDerivedType()); 111 } 112 113 QualType getPublicType(const Expr *E) { 114 auto *Cast = dyn_cast<ImplicitCastExpr>(E->IgnoreParens()); 115 if (Cast == nullptr || Cast->getCastKind() != CK_UncheckedDerivedToBase) { 116 QualType Ty = E->getType(); 117 if (Ty->isPointerType()) 118 return Ty->getPointeeType(); 119 return Ty; 120 } 121 122 // Is the derived type that we're casting from the type of `*this`? In this 123 // special case, we can upcast to the base class even if the base is 124 // non-public. 125 bool CastingFromThis = isa<CXXThisExpr>(Cast->getSubExpr()); 126 127 // Find the least-derived type in the path (i.e. the last entry in the list) 128 // that we can access. 129 const CXXBaseSpecifier *PublicBase = nullptr; 130 for (const CXXBaseSpecifier *Base : Cast->path()) { 131 if (Base->getAccessSpecifier() != AS_public && !CastingFromThis) 132 break; 133 PublicBase = Base; 134 CastingFromThis = false; 135 } 136 137 if (PublicBase != nullptr) 138 return PublicBase->getType(); 139 140 // We didn't find any public type that we could cast to. There may be more 141 // casts in `getSubExpr()`, so recurse. (If there aren't any more casts, this 142 // will return the type of `getSubExpr()`.) 143 return getPublicType(Cast->getSubExpr()); 144 } 145 146 // Returns the least-derived type for the receiver of `MCE` that 147 // `MCE.getImplicitObjectArgument()->IgnoreParentImpCasts()` can be downcast to. 148 // Effectively, we upcast until we reach a non-public base class, unless that 149 // base is a base of `*this`. 150 // 151 // This is needed to correctly match methods called on types derived from 152 // `std::optional`. 153 // 154 // Say we have a `struct Derived : public std::optional<int> {} d;` For a call 155 // `d.has_value()`, the `getImplicitObjectArgument()` looks like this: 156 // 157 // ImplicitCastExpr 'const std::__optional_storage_base<int>' lvalue 158 // | <UncheckedDerivedToBase (optional -> __optional_storage_base)> 159 // `-DeclRefExpr 'Derived' lvalue Var 'd' 'Derived' 160 // 161 // The type of the implicit object argument is `__optional_storage_base` 162 // (since this is the internal type that `has_value()` is declared on). If we 163 // call `IgnoreParenImpCasts()` on the implicit object argument, we get the 164 // `DeclRefExpr`, which has type `Derived`. Neither of these types is 165 // `optional`, and hence neither is sufficient for querying whether we are 166 // calling a method on `optional`. 167 // 168 // Instead, starting with the most derived type, we need to follow the chain of 169 // casts 170 QualType getPublicReceiverType(const CXXMemberCallExpr &MCE) { 171 return getPublicType(MCE.getImplicitObjectArgument()); 172 } 173 174 AST_MATCHER_P(CXXMemberCallExpr, publicReceiverType, 175 ast_matchers::internal::Matcher<QualType>, InnerMatcher) { 176 return InnerMatcher.matches(getPublicReceiverType(Node), Finder, Builder); 177 } 178 179 auto isOptionalMemberCallWithNameMatcher( 180 ast_matchers::internal::Matcher<NamedDecl> matcher, 181 const std::optional<StatementMatcher> &Ignorable = std::nullopt) { 182 return cxxMemberCallExpr(Ignorable ? on(expr(unless(*Ignorable))) 183 : anything(), 184 publicReceiverType(desugarsToOptionalType()), 185 callee(cxxMethodDecl(matcher))); 186 } 187 188 auto isOptionalOperatorCallWithName( 189 llvm::StringRef operator_name, 190 const std::optional<StatementMatcher> &Ignorable = std::nullopt) { 191 return cxxOperatorCallExpr( 192 hasOverloadedOperatorName(operator_name), 193 callee(cxxMethodDecl(ofClass(optionalClass()))), 194 Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr()); 195 } 196 197 auto isMakeOptionalCall() { 198 return callExpr(callee(functionDecl(hasAnyName( 199 "std::make_optional", "base::make_optional", 200 "absl::make_optional", "folly::make_optional"))), 201 hasOptionalType()); 202 } 203 204 auto nulloptTypeDecl() { 205 return namedDecl(hasAnyName("std::nullopt_t", "absl::nullopt_t", 206 "base::nullopt_t", "folly::None")); 207 } 208 209 auto hasNulloptType() { return hasType(nulloptTypeDecl()); } 210 211 auto inPlaceClass() { 212 return recordDecl(hasAnyName("std::in_place_t", "absl::in_place_t", 213 "base::in_place_t", "folly::in_place_t")); 214 } 215 216 auto isOptionalNulloptConstructor() { 217 return cxxConstructExpr( 218 hasDeclaration(cxxConstructorDecl(parameterCountIs(1), 219 hasParameter(0, hasNulloptType()))), 220 hasOptionalOrDerivedType()); 221 } 222 223 auto isOptionalInPlaceConstructor() { 224 return cxxConstructExpr(hasArgument(0, hasType(inPlaceClass())), 225 hasOptionalOrDerivedType()); 226 } 227 228 auto isOptionalValueOrConversionConstructor() { 229 return cxxConstructExpr( 230 unless(hasDeclaration( 231 cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))), 232 argumentCountIs(1), hasArgument(0, unless(hasNulloptType())), 233 hasOptionalOrDerivedType()); 234 } 235 236 auto isOptionalValueOrConversionAssignment() { 237 return cxxOperatorCallExpr( 238 hasOverloadedOperatorName("="), 239 callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))), 240 unless(hasDeclaration(cxxMethodDecl( 241 anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))), 242 argumentCountIs(2), hasArgument(1, unless(hasNulloptType()))); 243 } 244 245 auto isOptionalNulloptAssignment() { 246 return cxxOperatorCallExpr( 247 hasOverloadedOperatorName("="), 248 callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))), 249 argumentCountIs(2), hasArgument(1, hasNulloptType())); 250 } 251 252 auto isStdSwapCall() { 253 return callExpr(callee(functionDecl(hasName("std::swap"))), 254 argumentCountIs(2), 255 hasArgument(0, hasOptionalOrDerivedType()), 256 hasArgument(1, hasOptionalOrDerivedType())); 257 } 258 259 auto isStdForwardCall() { 260 return callExpr(callee(functionDecl(hasName("std::forward"))), 261 argumentCountIs(1), 262 hasArgument(0, hasOptionalOrDerivedType())); 263 } 264 265 constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall"; 266 267 auto isValueOrStringEmptyCall() { 268 // `opt.value_or("").empty()` 269 return cxxMemberCallExpr( 270 callee(cxxMethodDecl(hasName("empty"))), 271 onImplicitObjectArgument(ignoringImplicit( 272 cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 273 callee(cxxMethodDecl(hasName("value_or"), 274 ofClass(optionalClass()))), 275 hasArgument(0, stringLiteral(hasSize(0)))) 276 .bind(ValueOrCallID)))); 277 } 278 279 auto isValueOrNotEqX() { 280 auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) { 281 return hasOperands( 282 ignoringImplicit( 283 cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 284 callee(cxxMethodDecl(hasName("value_or"), 285 ofClass(optionalClass()))), 286 hasArgument(0, Arg)) 287 .bind(ValueOrCallID)), 288 ignoringImplicit(Arg)); 289 }; 290 291 // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd 292 // support this pattern for any expression, but the AST does not have a 293 // generic expression comparison facility, so we specialize to common cases 294 // seen in practice. FIXME: define a matcher that compares values across 295 // nodes, which would let us generalize this to any `X`. 296 return binaryOperation(hasOperatorName("!="), 297 anyOf(ComparesToSame(cxxNullPtrLiteralExpr()), 298 ComparesToSame(stringLiteral(hasSize(0))), 299 ComparesToSame(integerLiteral(equals(0))))); 300 } 301 302 auto isCallReturningOptional() { 303 return callExpr(hasType(qualType( 304 anyOf(desugarsToOptionalOrDerivedType(), 305 referenceType(pointee(desugarsToOptionalOrDerivedType())))))); 306 } 307 308 template <typename L, typename R> 309 auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) { 310 return cxxOperatorCallExpr( 311 anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")), 312 argumentCountIs(2), hasArgument(0, lhs_arg_matcher), 313 hasArgument(1, rhs_arg_matcher)); 314 } 315 316 /// Ensures that `Expr` is mapped to a `BoolValue` and returns its formula. 317 const Formula &forceBoolValue(Environment &Env, const Expr &Expr) { 318 auto *Value = Env.get<BoolValue>(Expr); 319 if (Value != nullptr) 320 return Value->formula(); 321 322 Value = &Env.makeAtomicBoolValue(); 323 Env.setValue(Expr, *Value); 324 return Value->formula(); 325 } 326 327 StorageLocation &locForHasValue(const RecordStorageLocation &OptionalLoc) { 328 return OptionalLoc.getSyntheticField("has_value"); 329 } 330 331 StorageLocation &locForValue(const RecordStorageLocation &OptionalLoc) { 332 return OptionalLoc.getSyntheticField("value"); 333 } 334 335 /// Sets `HasValueVal` as the symbolic value that represents the "has_value" 336 /// property of the optional at `OptionalLoc`. 337 void setHasValue(RecordStorageLocation &OptionalLoc, BoolValue &HasValueVal, 338 Environment &Env) { 339 Env.setValue(locForHasValue(OptionalLoc), HasValueVal); 340 } 341 342 /// Returns the symbolic value that represents the "has_value" property of the 343 /// optional at `OptionalLoc`. Returns null if `OptionalLoc` is null. 344 BoolValue *getHasValue(Environment &Env, RecordStorageLocation *OptionalLoc) { 345 if (OptionalLoc == nullptr) 346 return nullptr; 347 StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc); 348 auto *HasValueVal = Env.get<BoolValue>(HasValueLoc); 349 if (HasValueVal == nullptr) { 350 HasValueVal = &Env.makeAtomicBoolValue(); 351 Env.setValue(HasValueLoc, *HasValueVal); 352 } 353 return HasValueVal; 354 } 355 356 QualType valueTypeFromOptionalDecl(const CXXRecordDecl &RD) { 357 auto &CTSD = cast<ClassTemplateSpecializationDecl>(RD); 358 return CTSD.getTemplateArgs()[0].getAsType(); 359 } 360 361 /// Returns the number of optional wrappers in `Type`. 362 /// 363 /// For example, if `Type` is `optional<optional<int>>`, the result of this 364 /// function will be 2. 365 int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) { 366 const CXXRecordDecl *Optional = 367 getOptionalBaseClass(Type->getAsCXXRecordDecl()); 368 if (Optional == nullptr) 369 return 0; 370 return 1 + countOptionalWrappers( 371 ASTCtx, 372 valueTypeFromOptionalDecl(*Optional).getDesugaredType(ASTCtx)); 373 } 374 375 StorageLocation *getLocBehindPossiblePointer(const Expr &E, 376 const Environment &Env) { 377 if (E.isPRValue()) { 378 if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Env.getValue(E))) 379 return &PointerVal->getPointeeLoc(); 380 return nullptr; 381 } 382 return Env.getStorageLocation(E); 383 } 384 385 void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 386 LatticeTransferState &State) { 387 if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>( 388 getLocBehindPossiblePointer(*ObjectExpr, State.Env))) { 389 if (State.Env.getStorageLocation(*UnwrapExpr) == nullptr) 390 State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc)); 391 } 392 } 393 394 void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 395 LatticeTransferState &State) { 396 if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>( 397 getLocBehindPossiblePointer(*ObjectExpr, State.Env))) 398 State.Env.setValue( 399 *UnwrapExpr, State.Env.create<PointerValue>(locForValue(*OptionalLoc))); 400 } 401 402 void transferMakeOptionalCall(const CallExpr *E, 403 const MatchFinder::MatchResult &, 404 LatticeTransferState &State) { 405 setHasValue(State.Env.getResultObjectLocation(*E), 406 State.Env.getBoolLiteralValue(true), State.Env); 407 } 408 409 void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr, 410 const MatchFinder::MatchResult &, 411 LatticeTransferState &State) { 412 if (auto *HasValueVal = getHasValue( 413 State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) { 414 State.Env.setValue(*CallExpr, *HasValueVal); 415 } 416 } 417 418 /// `ModelPred` builds a logical formula relating the predicate in 419 /// `ValueOrPredExpr` to the optional's `has_value` property. 420 void transferValueOrImpl( 421 const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result, 422 LatticeTransferState &State, 423 const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal, 424 const Formula &HasValueVal)) { 425 auto &Env = State.Env; 426 427 const auto *MCE = 428 Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID); 429 430 auto *HasValueVal = 431 getHasValue(State.Env, getImplicitObjectLocation(*MCE, State.Env)); 432 if (HasValueVal == nullptr) 433 return; 434 435 Env.assume(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr), 436 HasValueVal->formula())); 437 } 438 439 void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr, 440 const MatchFinder::MatchResult &Result, 441 LatticeTransferState &State) { 442 return transferValueOrImpl(ComparisonExpr, Result, State, 443 [](Environment &Env, const Formula &ExprVal, 444 const Formula &HasValueVal) -> const Formula & { 445 auto &A = Env.arena(); 446 // If the result is *not* empty, then we know the 447 // optional must have been holding a value. If 448 // `ExprVal` is true, though, we don't learn 449 // anything definite about `has_value`, so we 450 // don't add any corresponding implications to 451 // the flow condition. 452 return A.makeImplies(A.makeNot(ExprVal), 453 HasValueVal); 454 }); 455 } 456 457 void transferValueOrNotEqX(const Expr *ComparisonExpr, 458 const MatchFinder::MatchResult &Result, 459 LatticeTransferState &State) { 460 transferValueOrImpl(ComparisonExpr, Result, State, 461 [](Environment &Env, const Formula &ExprVal, 462 const Formula &HasValueVal) -> const Formula & { 463 auto &A = Env.arena(); 464 // We know that if `(opt.value_or(X) != X)` then 465 // `opt.hasValue()`, even without knowing further 466 // details about the contents of `opt`. 467 return A.makeImplies(ExprVal, HasValueVal); 468 }); 469 } 470 471 void transferCallReturningOptional(const CallExpr *E, 472 const MatchFinder::MatchResult &Result, 473 LatticeTransferState &State) { 474 RecordStorageLocation *Loc = nullptr; 475 if (E->isPRValue()) { 476 Loc = &State.Env.getResultObjectLocation(*E); 477 } else { 478 Loc = State.Env.get<RecordStorageLocation>(*E); 479 if (Loc == nullptr) { 480 Loc = &cast<RecordStorageLocation>(State.Env.createStorageLocation(*E)); 481 State.Env.setStorageLocation(*E, *Loc); 482 } 483 } 484 485 if (State.Env.getValue(locForHasValue(*Loc)) != nullptr) 486 return; 487 488 setHasValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env); 489 } 490 491 void constructOptionalValue(const Expr &E, Environment &Env, 492 BoolValue &HasValueVal) { 493 RecordStorageLocation &Loc = Env.getResultObjectLocation(E); 494 setHasValue(Loc, HasValueVal, Env); 495 } 496 497 /// Returns a symbolic value for the "has_value" property of an `optional<T>` 498 /// value that is constructed/assigned from a value of type `U` or `optional<U>` 499 /// where `T` is constructible from `U`. 500 BoolValue &valueOrConversionHasValue(QualType DestType, const Expr &E, 501 const MatchFinder::MatchResult &MatchRes, 502 LatticeTransferState &State) { 503 const int DestTypeOptionalWrappersCount = 504 countOptionalWrappers(*MatchRes.Context, DestType); 505 const int ArgTypeOptionalWrappersCount = countOptionalWrappers( 506 *MatchRes.Context, E.getType().getNonReferenceType()); 507 508 // Is this an constructor of the form `template<class U> optional(U &&)` / 509 // assignment of the form `template<class U> optional& operator=(U &&)` 510 // (where `T` is assignable / constructible from `U`)? 511 // We recognize this because the number of optionals in the optional being 512 // assigned to is different from the function argument type. 513 if (DestTypeOptionalWrappersCount != ArgTypeOptionalWrappersCount) 514 return State.Env.getBoolLiteralValue(true); 515 516 // Otherwise, this must be a constructor of the form 517 // `template <class U> optional<optional<U> &&)` / assignment of the form 518 // `template <class U> optional& operator=(optional<U> &&) 519 // (where, again, `T` is assignable / constructible from `U`). 520 auto *Loc = State.Env.get<RecordStorageLocation>(E); 521 if (auto *HasValueVal = getHasValue(State.Env, Loc)) 522 return *HasValueVal; 523 return State.Env.makeAtomicBoolValue(); 524 } 525 526 void transferValueOrConversionConstructor( 527 const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes, 528 LatticeTransferState &State) { 529 assert(E->getNumArgs() > 0); 530 531 constructOptionalValue( 532 *E, State.Env, 533 valueOrConversionHasValue( 534 E->getConstructor()->getThisType()->getPointeeType(), *E->getArg(0), 535 MatchRes, State)); 536 } 537 538 void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal, 539 LatticeTransferState &State) { 540 assert(E->getNumArgs() > 0); 541 542 if (auto *Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0))) { 543 setHasValue(*Loc, HasValueVal, State.Env); 544 545 // Assign a storage location for the whole expression. 546 State.Env.setStorageLocation(*E, *Loc); 547 } 548 } 549 550 void transferValueOrConversionAssignment( 551 const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes, 552 LatticeTransferState &State) { 553 assert(E->getNumArgs() > 1); 554 transferAssignment( 555 E, 556 valueOrConversionHasValue(E->getArg(0)->getType().getNonReferenceType(), 557 *E->getArg(1), MatchRes, State), 558 State); 559 } 560 561 void transferNulloptAssignment(const CXXOperatorCallExpr *E, 562 const MatchFinder::MatchResult &, 563 LatticeTransferState &State) { 564 transferAssignment(E, State.Env.getBoolLiteralValue(false), State); 565 } 566 567 void transferSwap(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2, 568 Environment &Env) { 569 // We account for cases where one or both of the optionals are not modeled, 570 // either lacking associated storage locations, or lacking values associated 571 // to such storage locations. 572 573 if (Loc1 == nullptr) { 574 if (Loc2 != nullptr) 575 setHasValue(*Loc2, Env.makeAtomicBoolValue(), Env); 576 return; 577 } 578 if (Loc2 == nullptr) { 579 setHasValue(*Loc1, Env.makeAtomicBoolValue(), Env); 580 return; 581 } 582 583 // Both expressions have locations, though they may not have corresponding 584 // values. In that case, we create a fresh value at this point. Note that if 585 // two branches both do this, they will not share the value, but it at least 586 // allows for local reasoning about the value. To avoid the above, we would 587 // need *lazy* value allocation. 588 // FIXME: allocate values lazily, instead of just creating a fresh value. 589 BoolValue *BoolVal1 = getHasValue(Env, Loc1); 590 if (BoolVal1 == nullptr) 591 BoolVal1 = &Env.makeAtomicBoolValue(); 592 593 BoolValue *BoolVal2 = getHasValue(Env, Loc2); 594 if (BoolVal2 == nullptr) 595 BoolVal2 = &Env.makeAtomicBoolValue(); 596 597 setHasValue(*Loc1, *BoolVal2, Env); 598 setHasValue(*Loc2, *BoolVal1, Env); 599 } 600 601 void transferSwapCall(const CXXMemberCallExpr *E, 602 const MatchFinder::MatchResult &, 603 LatticeTransferState &State) { 604 assert(E->getNumArgs() == 1); 605 auto *OtherLoc = State.Env.get<RecordStorageLocation>(*E->getArg(0)); 606 transferSwap(getImplicitObjectLocation(*E, State.Env), OtherLoc, State.Env); 607 } 608 609 void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &, 610 LatticeTransferState &State) { 611 assert(E->getNumArgs() == 2); 612 auto *Arg0Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0)); 613 auto *Arg1Loc = State.Env.get<RecordStorageLocation>(*E->getArg(1)); 614 transferSwap(Arg0Loc, Arg1Loc, State.Env); 615 } 616 617 void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &, 618 LatticeTransferState &State) { 619 assert(E->getNumArgs() == 1); 620 621 if (auto *Loc = State.Env.getStorageLocation(*E->getArg(0))) 622 State.Env.setStorageLocation(*E, *Loc); 623 } 624 625 const Formula &evaluateEquality(Arena &A, const Formula &EqVal, 626 const Formula &LHS, const Formula &RHS) { 627 // Logically, an optional<T> object is composed of two values - a `has_value` 628 // bit and a value of type T. Equality of optional objects compares both 629 // values. Therefore, merely comparing the `has_value` bits isn't sufficient: 630 // when two optional objects are engaged, the equality of their respective 631 // values of type T matters. Since we only track the `has_value` bits, we 632 // can't make any conclusions about equality when we know that two optional 633 // objects are engaged. 634 // 635 // We express this as two facts about the equality: 636 // a) EqVal => (LHS & RHS) v (!RHS & !LHS) 637 // If they are equal, then either both are set or both are unset. 638 // b) (!LHS & !RHS) => EqVal 639 // If neither is set, then they are equal. 640 // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula. 641 return A.makeAnd( 642 A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS), 643 A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))), 644 A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS))); 645 } 646 647 void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr, 648 const MatchFinder::MatchResult &, 649 LatticeTransferState &State) { 650 Environment &Env = State.Env; 651 auto &A = Env.arena(); 652 auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 653 auto *Arg0Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(0)); 654 if (auto *LHasVal = getHasValue(Env, Arg0Loc)) { 655 auto *Arg1Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(1)); 656 if (auto *RHasVal = getHasValue(Env, Arg1Loc)) { 657 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 658 CmpValue = &A.makeNot(*CmpValue); 659 Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(), 660 RHasVal->formula())); 661 } 662 } 663 } 664 665 void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr, 666 const clang::Expr *E, Environment &Env) { 667 auto &A = Env.arena(); 668 auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 669 auto *Loc = Env.get<RecordStorageLocation>(*E); 670 if (auto *HasVal = getHasValue(Env, Loc)) { 671 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 672 CmpValue = &A.makeNot(*CmpValue); 673 Env.assume( 674 evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true))); 675 } 676 } 677 678 void transferOptionalAndNulloptCmp(const clang::CXXOperatorCallExpr *CmpExpr, 679 const clang::Expr *E, Environment &Env) { 680 auto &A = Env.arena(); 681 auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 682 auto *Loc = Env.get<RecordStorageLocation>(*E); 683 if (auto *HasVal = getHasValue(Env, Loc)) { 684 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 685 CmpValue = &A.makeNot(*CmpValue); 686 Env.assume(evaluateEquality(A, *CmpValue, HasVal->formula(), 687 A.makeLiteral(false))); 688 } 689 } 690 691 std::optional<StatementMatcher> 692 ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) { 693 if (Options.IgnoreSmartPointerDereference) { 694 auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr( 695 anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")), 696 unless(hasArgument(0, expr(hasOptionalType())))))); 697 return expr( 698 anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse)))); 699 } 700 return std::nullopt; 701 } 702 703 StatementMatcher 704 valueCall(const std::optional<StatementMatcher> &IgnorableOptional) { 705 return isOptionalMemberCallWithNameMatcher(hasName("value"), 706 IgnorableOptional); 707 } 708 709 StatementMatcher 710 valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) { 711 return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional), 712 isOptionalOperatorCallWithName("->", IgnorableOptional))); 713 } 714 715 auto buildTransferMatchSwitch() { 716 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 717 // lot of duplicated work (e.g. string comparisons), consider providing APIs 718 // that avoid it through memoization. 719 return CFGMatchSwitchBuilder<LatticeTransferState>() 720 // make_optional 721 .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall) 722 723 // optional::optional (in place) 724 .CaseOfCFGStmt<CXXConstructExpr>( 725 isOptionalInPlaceConstructor(), 726 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 727 LatticeTransferState &State) { 728 constructOptionalValue(*E, State.Env, 729 State.Env.getBoolLiteralValue(true)); 730 }) 731 // optional::optional(nullopt_t) 732 .CaseOfCFGStmt<CXXConstructExpr>( 733 isOptionalNulloptConstructor(), 734 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 735 LatticeTransferState &State) { 736 constructOptionalValue(*E, State.Env, 737 State.Env.getBoolLiteralValue(false)); 738 }) 739 // optional::optional (value/conversion) 740 .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(), 741 transferValueOrConversionConstructor) 742 743 // optional::operator= 744 .CaseOfCFGStmt<CXXOperatorCallExpr>( 745 isOptionalValueOrConversionAssignment(), 746 transferValueOrConversionAssignment) 747 .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(), 748 transferNulloptAssignment) 749 750 // optional::value 751 .CaseOfCFGStmt<CXXMemberCallExpr>( 752 valueCall(std::nullopt), 753 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 754 LatticeTransferState &State) { 755 transferUnwrapCall(E, E->getImplicitObjectArgument(), State); 756 }) 757 758 // optional::operator* 759 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"), 760 [](const CallExpr *E, 761 const MatchFinder::MatchResult &, 762 LatticeTransferState &State) { 763 transferUnwrapCall(E, E->getArg(0), State); 764 }) 765 766 // optional::operator-> 767 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"), 768 [](const CallExpr *E, 769 const MatchFinder::MatchResult &, 770 LatticeTransferState &State) { 771 transferArrowOpCall(E, E->getArg(0), State); 772 }) 773 774 // optional::has_value, optional::hasValue 775 // Of the supported optionals only folly::Optional uses hasValue, but this 776 // will also pass for other types 777 .CaseOfCFGStmt<CXXMemberCallExpr>( 778 isOptionalMemberCallWithNameMatcher( 779 hasAnyName("has_value", "hasValue")), 780 transferOptionalHasValueCall) 781 782 // optional::operator bool 783 .CaseOfCFGStmt<CXXMemberCallExpr>( 784 isOptionalMemberCallWithNameMatcher(hasName("operator bool")), 785 transferOptionalHasValueCall) 786 787 // optional::emplace 788 .CaseOfCFGStmt<CXXMemberCallExpr>( 789 isOptionalMemberCallWithNameMatcher(hasName("emplace")), 790 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 791 LatticeTransferState &State) { 792 if (RecordStorageLocation *Loc = 793 getImplicitObjectLocation(*E, State.Env)) { 794 setHasValue(*Loc, State.Env.getBoolLiteralValue(true), State.Env); 795 } 796 }) 797 798 // optional::reset 799 .CaseOfCFGStmt<CXXMemberCallExpr>( 800 isOptionalMemberCallWithNameMatcher(hasName("reset")), 801 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 802 LatticeTransferState &State) { 803 if (RecordStorageLocation *Loc = 804 getImplicitObjectLocation(*E, State.Env)) { 805 setHasValue(*Loc, State.Env.getBoolLiteralValue(false), 806 State.Env); 807 } 808 }) 809 810 // optional::swap 811 .CaseOfCFGStmt<CXXMemberCallExpr>( 812 isOptionalMemberCallWithNameMatcher(hasName("swap")), 813 transferSwapCall) 814 815 // std::swap 816 .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall) 817 818 // std::forward 819 .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall) 820 821 // opt.value_or("").empty() 822 .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(), 823 transferValueOrStringEmptyCall) 824 825 // opt.value_or(X) != X 826 .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX) 827 828 // Comparisons (==, !=): 829 .CaseOfCFGStmt<CXXOperatorCallExpr>( 830 isComparisonOperatorCall(hasOptionalType(), hasOptionalType()), 831 transferOptionalAndOptionalCmp) 832 .CaseOfCFGStmt<CXXOperatorCallExpr>( 833 isComparisonOperatorCall(hasOptionalType(), hasNulloptType()), 834 [](const clang::CXXOperatorCallExpr *Cmp, 835 const MatchFinder::MatchResult &, LatticeTransferState &State) { 836 transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(0), State.Env); 837 }) 838 .CaseOfCFGStmt<CXXOperatorCallExpr>( 839 isComparisonOperatorCall(hasNulloptType(), hasOptionalType()), 840 [](const clang::CXXOperatorCallExpr *Cmp, 841 const MatchFinder::MatchResult &, LatticeTransferState &State) { 842 transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(1), State.Env); 843 }) 844 .CaseOfCFGStmt<CXXOperatorCallExpr>( 845 isComparisonOperatorCall( 846 hasOptionalType(), 847 unless(anyOf(hasOptionalType(), hasNulloptType()))), 848 [](const clang::CXXOperatorCallExpr *Cmp, 849 const MatchFinder::MatchResult &, LatticeTransferState &State) { 850 transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env); 851 }) 852 .CaseOfCFGStmt<CXXOperatorCallExpr>( 853 isComparisonOperatorCall( 854 unless(anyOf(hasOptionalType(), hasNulloptType())), 855 hasOptionalType()), 856 [](const clang::CXXOperatorCallExpr *Cmp, 857 const MatchFinder::MatchResult &, LatticeTransferState &State) { 858 transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env); 859 }) 860 861 // returns optional 862 .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(), 863 transferCallReturningOptional) 864 865 .Build(); 866 } 867 868 llvm::SmallVector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr, 869 const Environment &Env) { 870 if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>( 871 getLocBehindPossiblePointer(*ObjectExpr, Env))) { 872 auto *Prop = Env.getValue(locForHasValue(*OptionalLoc)); 873 if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) { 874 if (Env.proves(HasValueVal->formula())) 875 return {}; 876 } 877 } 878 879 // Record that this unwrap is *not* provably safe. 880 // FIXME: include either the name of the optional (if applicable) or a source 881 // range of the access for easier interpretation of the result. 882 return {ObjectExpr->getBeginLoc()}; 883 } 884 885 auto buildDiagnoseMatchSwitch( 886 const UncheckedOptionalAccessModelOptions &Options) { 887 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 888 // lot of duplicated work (e.g. string comparisons), consider providing APIs 889 // that avoid it through memoization. 890 auto IgnorableOptional = ignorableOptional(Options); 891 return CFGMatchSwitchBuilder<const Environment, 892 llvm::SmallVector<SourceLocation>>() 893 // optional::value 894 .CaseOfCFGStmt<CXXMemberCallExpr>( 895 valueCall(IgnorableOptional), 896 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 897 const Environment &Env) { 898 return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env); 899 }) 900 901 // optional::operator*, optional::operator-> 902 .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional), 903 [](const CallExpr *E, 904 const MatchFinder::MatchResult &, 905 const Environment &Env) { 906 return diagnoseUnwrapCall(E->getArg(0), Env); 907 }) 908 .Build(); 909 } 910 911 } // namespace 912 913 ast_matchers::DeclarationMatcher 914 UncheckedOptionalAccessModel::optionalClassDecl() { 915 return cxxRecordDecl(optionalClass()); 916 } 917 918 UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx, 919 Environment &Env) 920 : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx), 921 TransferMatchSwitch(buildTransferMatchSwitch()) { 922 Env.getDataflowAnalysisContext().setSyntheticFieldCallback( 923 [&Ctx](QualType Ty) -> llvm::StringMap<QualType> { 924 const CXXRecordDecl *Optional = 925 getOptionalBaseClass(Ty->getAsCXXRecordDecl()); 926 if (Optional == nullptr) 927 return {}; 928 return {{"value", valueTypeFromOptionalDecl(*Optional)}, 929 {"has_value", Ctx.BoolTy}}; 930 }); 931 } 932 933 void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt, 934 NoopLattice &L, Environment &Env) { 935 LatticeTransferState State(L, Env); 936 TransferMatchSwitch(Elt, getASTContext(), State); 937 } 938 939 UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser( 940 UncheckedOptionalAccessModelOptions Options) 941 : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {} 942 943 } // namespace dataflow 944 } // namespace clang 945