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 /// Creates a symbolic value for an `optional` value at an existing storage 343 /// location. Uses `HasValueVal` as the symbolic value of the "has_value" 344 /// property. 345 RecordValue &createOptionalValue(RecordStorageLocation &Loc, 346 BoolValue &HasValueVal, Environment &Env) { 347 auto &OptionalVal = Env.create<RecordValue>(Loc); 348 Env.setValue(Loc, OptionalVal); 349 setHasValue(Loc, HasValueVal, Env); 350 return OptionalVal; 351 } 352 353 /// Returns the symbolic value that represents the "has_value" property of the 354 /// optional at `OptionalLoc`. Returns null if `OptionalLoc` is null. 355 BoolValue *getHasValue(Environment &Env, RecordStorageLocation *OptionalLoc) { 356 if (OptionalLoc == nullptr) 357 return nullptr; 358 StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc); 359 auto *HasValueVal = Env.get<BoolValue>(HasValueLoc); 360 if (HasValueVal == nullptr) { 361 HasValueVal = &Env.makeAtomicBoolValue(); 362 Env.setValue(HasValueLoc, *HasValueVal); 363 } 364 return HasValueVal; 365 } 366 367 QualType valueTypeFromOptionalDecl(const CXXRecordDecl &RD) { 368 auto &CTSD = cast<ClassTemplateSpecializationDecl>(RD); 369 return CTSD.getTemplateArgs()[0].getAsType(); 370 } 371 372 /// Returns the number of optional wrappers in `Type`. 373 /// 374 /// For example, if `Type` is `optional<optional<int>>`, the result of this 375 /// function will be 2. 376 int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) { 377 const CXXRecordDecl *Optional = 378 getOptionalBaseClass(Type->getAsCXXRecordDecl()); 379 if (Optional == nullptr) 380 return 0; 381 return 1 + countOptionalWrappers( 382 ASTCtx, 383 valueTypeFromOptionalDecl(*Optional).getDesugaredType(ASTCtx)); 384 } 385 386 StorageLocation *getLocBehindPossiblePointer(const Expr &E, 387 const Environment &Env) { 388 if (E.isPRValue()) { 389 if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Env.getValue(E))) 390 return &PointerVal->getPointeeLoc(); 391 return nullptr; 392 } 393 return Env.getStorageLocation(E); 394 } 395 396 void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 397 LatticeTransferState &State) { 398 if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>( 399 getLocBehindPossiblePointer(*ObjectExpr, State.Env))) { 400 if (State.Env.getStorageLocation(*UnwrapExpr) == nullptr) 401 State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc)); 402 } 403 } 404 405 void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 406 LatticeTransferState &State) { 407 if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>( 408 getLocBehindPossiblePointer(*ObjectExpr, State.Env))) 409 State.Env.setValue( 410 *UnwrapExpr, State.Env.create<PointerValue>(locForValue(*OptionalLoc))); 411 } 412 413 void transferMakeOptionalCall(const CallExpr *E, 414 const MatchFinder::MatchResult &, 415 LatticeTransferState &State) { 416 State.Env.setValue( 417 *E, createOptionalValue(State.Env.getResultObjectLocation(*E), 418 State.Env.getBoolLiteralValue(true), State.Env)); 419 } 420 421 void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr, 422 const MatchFinder::MatchResult &, 423 LatticeTransferState &State) { 424 if (auto *HasValueVal = getHasValue( 425 State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) { 426 State.Env.setValue(*CallExpr, *HasValueVal); 427 } 428 } 429 430 /// `ModelPred` builds a logical formula relating the predicate in 431 /// `ValueOrPredExpr` to the optional's `has_value` property. 432 void transferValueOrImpl( 433 const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result, 434 LatticeTransferState &State, 435 const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal, 436 const Formula &HasValueVal)) { 437 auto &Env = State.Env; 438 439 const auto *MCE = 440 Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID); 441 442 auto *HasValueVal = 443 getHasValue(State.Env, getImplicitObjectLocation(*MCE, State.Env)); 444 if (HasValueVal == nullptr) 445 return; 446 447 Env.assume(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr), 448 HasValueVal->formula())); 449 } 450 451 void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr, 452 const MatchFinder::MatchResult &Result, 453 LatticeTransferState &State) { 454 return transferValueOrImpl(ComparisonExpr, Result, State, 455 [](Environment &Env, const Formula &ExprVal, 456 const Formula &HasValueVal) -> const Formula & { 457 auto &A = Env.arena(); 458 // If the result is *not* empty, then we know the 459 // optional must have been holding a value. If 460 // `ExprVal` is true, though, we don't learn 461 // anything definite about `has_value`, so we 462 // don't add any corresponding implications to 463 // the flow condition. 464 return A.makeImplies(A.makeNot(ExprVal), 465 HasValueVal); 466 }); 467 } 468 469 void transferValueOrNotEqX(const Expr *ComparisonExpr, 470 const MatchFinder::MatchResult &Result, 471 LatticeTransferState &State) { 472 transferValueOrImpl(ComparisonExpr, Result, State, 473 [](Environment &Env, const Formula &ExprVal, 474 const Formula &HasValueVal) -> const Formula & { 475 auto &A = Env.arena(); 476 // We know that if `(opt.value_or(X) != X)` then 477 // `opt.hasValue()`, even without knowing further 478 // details about the contents of `opt`. 479 return A.makeImplies(ExprVal, HasValueVal); 480 }); 481 } 482 483 void transferCallReturningOptional(const CallExpr *E, 484 const MatchFinder::MatchResult &Result, 485 LatticeTransferState &State) { 486 if (State.Env.getValue(*E) != nullptr) 487 return; 488 489 RecordStorageLocation *Loc = nullptr; 490 if (E->isPRValue()) { 491 Loc = &State.Env.getResultObjectLocation(*E); 492 } else { 493 Loc = State.Env.get<RecordStorageLocation>(*E); 494 if (Loc == nullptr) { 495 Loc = &cast<RecordStorageLocation>(State.Env.createStorageLocation(*E)); 496 State.Env.setStorageLocation(*E, *Loc); 497 } 498 } 499 500 RecordValue &Val = 501 createOptionalValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env); 502 if (E->isPRValue()) 503 State.Env.setValue(*E, Val); 504 } 505 506 void constructOptionalValue(const Expr &E, Environment &Env, 507 BoolValue &HasValueVal) { 508 RecordStorageLocation &Loc = Env.getResultObjectLocation(E); 509 Env.setValue(E, createOptionalValue(Loc, HasValueVal, Env)); 510 } 511 512 /// Returns a symbolic value for the "has_value" property of an `optional<T>` 513 /// value that is constructed/assigned from a value of type `U` or `optional<U>` 514 /// where `T` is constructible from `U`. 515 BoolValue &valueOrConversionHasValue(QualType DestType, const Expr &E, 516 const MatchFinder::MatchResult &MatchRes, 517 LatticeTransferState &State) { 518 const int DestTypeOptionalWrappersCount = 519 countOptionalWrappers(*MatchRes.Context, DestType); 520 const int ArgTypeOptionalWrappersCount = countOptionalWrappers( 521 *MatchRes.Context, E.getType().getNonReferenceType()); 522 523 // Is this an constructor of the form `template<class U> optional(U &&)` / 524 // assignment of the form `template<class U> optional& operator=(U &&)` 525 // (where `T` is assignable / constructible from `U`)? 526 // We recognize this because the number of optionals in the optional being 527 // assigned to is different from the function argument type. 528 if (DestTypeOptionalWrappersCount != ArgTypeOptionalWrappersCount) 529 return State.Env.getBoolLiteralValue(true); 530 531 // Otherwise, this must be a constructor of the form 532 // `template <class U> optional<optional<U> &&)` / assignment of the form 533 // `template <class U> optional& operator=(optional<U> &&) 534 // (where, again, `T` is assignable / constructible from `U`). 535 auto *Loc = State.Env.get<RecordStorageLocation>(E); 536 if (auto *HasValueVal = getHasValue(State.Env, Loc)) 537 return *HasValueVal; 538 return State.Env.makeAtomicBoolValue(); 539 } 540 541 void transferValueOrConversionConstructor( 542 const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes, 543 LatticeTransferState &State) { 544 assert(E->getNumArgs() > 0); 545 546 constructOptionalValue( 547 *E, State.Env, 548 valueOrConversionHasValue( 549 E->getConstructor()->getThisType()->getPointeeType(), *E->getArg(0), 550 MatchRes, State)); 551 } 552 553 void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal, 554 LatticeTransferState &State) { 555 assert(E->getNumArgs() > 0); 556 557 if (auto *Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0))) { 558 createOptionalValue(*Loc, HasValueVal, State.Env); 559 560 // Assign a storage location for the whole expression. 561 State.Env.setStorageLocation(*E, *Loc); 562 } 563 } 564 565 void transferValueOrConversionAssignment( 566 const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes, 567 LatticeTransferState &State) { 568 assert(E->getNumArgs() > 1); 569 transferAssignment( 570 E, 571 valueOrConversionHasValue(E->getArg(0)->getType().getNonReferenceType(), 572 *E->getArg(1), MatchRes, State), 573 State); 574 } 575 576 void transferNulloptAssignment(const CXXOperatorCallExpr *E, 577 const MatchFinder::MatchResult &, 578 LatticeTransferState &State) { 579 transferAssignment(E, State.Env.getBoolLiteralValue(false), State); 580 } 581 582 void transferSwap(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2, 583 Environment &Env) { 584 // We account for cases where one or both of the optionals are not modeled, 585 // either lacking associated storage locations, or lacking values associated 586 // to such storage locations. 587 588 if (Loc1 == nullptr) { 589 if (Loc2 != nullptr) 590 createOptionalValue(*Loc2, Env.makeAtomicBoolValue(), Env); 591 return; 592 } 593 if (Loc2 == nullptr) { 594 createOptionalValue(*Loc1, Env.makeAtomicBoolValue(), Env); 595 return; 596 } 597 598 // Both expressions have locations, though they may not have corresponding 599 // values. In that case, we create a fresh value at this point. Note that if 600 // two branches both do this, they will not share the value, but it at least 601 // allows for local reasoning about the value. To avoid the above, we would 602 // need *lazy* value allocation. 603 // FIXME: allocate values lazily, instead of just creating a fresh value. 604 BoolValue *BoolVal1 = getHasValue(Env, Loc1); 605 if (BoolVal1 == nullptr) 606 BoolVal1 = &Env.makeAtomicBoolValue(); 607 608 BoolValue *BoolVal2 = getHasValue(Env, Loc2); 609 if (BoolVal2 == nullptr) 610 BoolVal2 = &Env.makeAtomicBoolValue(); 611 612 createOptionalValue(*Loc1, *BoolVal2, Env); 613 createOptionalValue(*Loc2, *BoolVal1, Env); 614 } 615 616 void transferSwapCall(const CXXMemberCallExpr *E, 617 const MatchFinder::MatchResult &, 618 LatticeTransferState &State) { 619 assert(E->getNumArgs() == 1); 620 auto *OtherLoc = State.Env.get<RecordStorageLocation>(*E->getArg(0)); 621 transferSwap(getImplicitObjectLocation(*E, State.Env), OtherLoc, State.Env); 622 } 623 624 void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &, 625 LatticeTransferState &State) { 626 assert(E->getNumArgs() == 2); 627 auto *Arg0Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0)); 628 auto *Arg1Loc = State.Env.get<RecordStorageLocation>(*E->getArg(1)); 629 transferSwap(Arg0Loc, Arg1Loc, State.Env); 630 } 631 632 void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &, 633 LatticeTransferState &State) { 634 assert(E->getNumArgs() == 1); 635 636 if (auto *Loc = State.Env.getStorageLocation(*E->getArg(0))) 637 State.Env.setStorageLocation(*E, *Loc); 638 } 639 640 const Formula &evaluateEquality(Arena &A, const Formula &EqVal, 641 const Formula &LHS, const Formula &RHS) { 642 // Logically, an optional<T> object is composed of two values - a `has_value` 643 // bit and a value of type T. Equality of optional objects compares both 644 // values. Therefore, merely comparing the `has_value` bits isn't sufficient: 645 // when two optional objects are engaged, the equality of their respective 646 // values of type T matters. Since we only track the `has_value` bits, we 647 // can't make any conclusions about equality when we know that two optional 648 // objects are engaged. 649 // 650 // We express this as two facts about the equality: 651 // a) EqVal => (LHS & RHS) v (!RHS & !LHS) 652 // If they are equal, then either both are set or both are unset. 653 // b) (!LHS & !RHS) => EqVal 654 // If neither is set, then they are equal. 655 // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula. 656 return A.makeAnd( 657 A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS), 658 A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))), 659 A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS))); 660 } 661 662 void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr, 663 const MatchFinder::MatchResult &, 664 LatticeTransferState &State) { 665 Environment &Env = State.Env; 666 auto &A = Env.arena(); 667 auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 668 auto *Arg0Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(0)); 669 if (auto *LHasVal = getHasValue(Env, Arg0Loc)) { 670 auto *Arg1Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(1)); 671 if (auto *RHasVal = getHasValue(Env, Arg1Loc)) { 672 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 673 CmpValue = &A.makeNot(*CmpValue); 674 Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(), 675 RHasVal->formula())); 676 } 677 } 678 } 679 680 void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr, 681 const clang::Expr *E, Environment &Env) { 682 auto &A = Env.arena(); 683 auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 684 auto *Loc = Env.get<RecordStorageLocation>(*E); 685 if (auto *HasVal = getHasValue(Env, Loc)) { 686 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 687 CmpValue = &A.makeNot(*CmpValue); 688 Env.assume( 689 evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true))); 690 } 691 } 692 693 void transferOptionalAndNulloptCmp(const clang::CXXOperatorCallExpr *CmpExpr, 694 const clang::Expr *E, Environment &Env) { 695 auto &A = Env.arena(); 696 auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 697 auto *Loc = Env.get<RecordStorageLocation>(*E); 698 if (auto *HasVal = getHasValue(Env, Loc)) { 699 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 700 CmpValue = &A.makeNot(*CmpValue); 701 Env.assume(evaluateEquality(A, *CmpValue, HasVal->formula(), 702 A.makeLiteral(false))); 703 } 704 } 705 706 std::optional<StatementMatcher> 707 ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) { 708 if (Options.IgnoreSmartPointerDereference) { 709 auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr( 710 anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")), 711 unless(hasArgument(0, expr(hasOptionalType())))))); 712 return expr( 713 anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse)))); 714 } 715 return std::nullopt; 716 } 717 718 StatementMatcher 719 valueCall(const std::optional<StatementMatcher> &IgnorableOptional) { 720 return isOptionalMemberCallWithNameMatcher(hasName("value"), 721 IgnorableOptional); 722 } 723 724 StatementMatcher 725 valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) { 726 return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional), 727 isOptionalOperatorCallWithName("->", IgnorableOptional))); 728 } 729 730 auto buildTransferMatchSwitch() { 731 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 732 // lot of duplicated work (e.g. string comparisons), consider providing APIs 733 // that avoid it through memoization. 734 return CFGMatchSwitchBuilder<LatticeTransferState>() 735 // make_optional 736 .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall) 737 738 // optional::optional (in place) 739 .CaseOfCFGStmt<CXXConstructExpr>( 740 isOptionalInPlaceConstructor(), 741 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 742 LatticeTransferState &State) { 743 constructOptionalValue(*E, State.Env, 744 State.Env.getBoolLiteralValue(true)); 745 }) 746 // optional::optional(nullopt_t) 747 .CaseOfCFGStmt<CXXConstructExpr>( 748 isOptionalNulloptConstructor(), 749 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 750 LatticeTransferState &State) { 751 constructOptionalValue(*E, State.Env, 752 State.Env.getBoolLiteralValue(false)); 753 }) 754 // optional::optional (value/conversion) 755 .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(), 756 transferValueOrConversionConstructor) 757 758 // optional::operator= 759 .CaseOfCFGStmt<CXXOperatorCallExpr>( 760 isOptionalValueOrConversionAssignment(), 761 transferValueOrConversionAssignment) 762 .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(), 763 transferNulloptAssignment) 764 765 // optional::value 766 .CaseOfCFGStmt<CXXMemberCallExpr>( 767 valueCall(std::nullopt), 768 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 769 LatticeTransferState &State) { 770 transferUnwrapCall(E, E->getImplicitObjectArgument(), State); 771 }) 772 773 // optional::operator* 774 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"), 775 [](const CallExpr *E, 776 const MatchFinder::MatchResult &, 777 LatticeTransferState &State) { 778 transferUnwrapCall(E, E->getArg(0), State); 779 }) 780 781 // optional::operator-> 782 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"), 783 [](const CallExpr *E, 784 const MatchFinder::MatchResult &, 785 LatticeTransferState &State) { 786 transferArrowOpCall(E, E->getArg(0), State); 787 }) 788 789 // optional::has_value, optional::hasValue 790 // Of the supported optionals only folly::Optional uses hasValue, but this 791 // will also pass for other types 792 .CaseOfCFGStmt<CXXMemberCallExpr>( 793 isOptionalMemberCallWithNameMatcher( 794 hasAnyName("has_value", "hasValue")), 795 transferOptionalHasValueCall) 796 797 // optional::operator bool 798 .CaseOfCFGStmt<CXXMemberCallExpr>( 799 isOptionalMemberCallWithNameMatcher(hasName("operator bool")), 800 transferOptionalHasValueCall) 801 802 // optional::emplace 803 .CaseOfCFGStmt<CXXMemberCallExpr>( 804 isOptionalMemberCallWithNameMatcher(hasName("emplace")), 805 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 806 LatticeTransferState &State) { 807 if (RecordStorageLocation *Loc = 808 getImplicitObjectLocation(*E, State.Env)) { 809 createOptionalValue(*Loc, State.Env.getBoolLiteralValue(true), 810 State.Env); 811 } 812 }) 813 814 // optional::reset 815 .CaseOfCFGStmt<CXXMemberCallExpr>( 816 isOptionalMemberCallWithNameMatcher(hasName("reset")), 817 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 818 LatticeTransferState &State) { 819 if (RecordStorageLocation *Loc = 820 getImplicitObjectLocation(*E, State.Env)) { 821 createOptionalValue(*Loc, State.Env.getBoolLiteralValue(false), 822 State.Env); 823 } 824 }) 825 826 // optional::swap 827 .CaseOfCFGStmt<CXXMemberCallExpr>( 828 isOptionalMemberCallWithNameMatcher(hasName("swap")), 829 transferSwapCall) 830 831 // std::swap 832 .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall) 833 834 // std::forward 835 .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall) 836 837 // opt.value_or("").empty() 838 .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(), 839 transferValueOrStringEmptyCall) 840 841 // opt.value_or(X) != X 842 .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX) 843 844 // Comparisons (==, !=): 845 .CaseOfCFGStmt<CXXOperatorCallExpr>( 846 isComparisonOperatorCall(hasOptionalType(), hasOptionalType()), 847 transferOptionalAndOptionalCmp) 848 .CaseOfCFGStmt<CXXOperatorCallExpr>( 849 isComparisonOperatorCall(hasOptionalType(), hasNulloptType()), 850 [](const clang::CXXOperatorCallExpr *Cmp, 851 const MatchFinder::MatchResult &, LatticeTransferState &State) { 852 transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(0), State.Env); 853 }) 854 .CaseOfCFGStmt<CXXOperatorCallExpr>( 855 isComparisonOperatorCall(hasNulloptType(), hasOptionalType()), 856 [](const clang::CXXOperatorCallExpr *Cmp, 857 const MatchFinder::MatchResult &, LatticeTransferState &State) { 858 transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(1), State.Env); 859 }) 860 .CaseOfCFGStmt<CXXOperatorCallExpr>( 861 isComparisonOperatorCall( 862 hasOptionalType(), 863 unless(anyOf(hasOptionalType(), hasNulloptType()))), 864 [](const clang::CXXOperatorCallExpr *Cmp, 865 const MatchFinder::MatchResult &, LatticeTransferState &State) { 866 transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env); 867 }) 868 .CaseOfCFGStmt<CXXOperatorCallExpr>( 869 isComparisonOperatorCall( 870 unless(anyOf(hasOptionalType(), hasNulloptType())), 871 hasOptionalType()), 872 [](const clang::CXXOperatorCallExpr *Cmp, 873 const MatchFinder::MatchResult &, LatticeTransferState &State) { 874 transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env); 875 }) 876 877 // returns optional 878 .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(), 879 transferCallReturningOptional) 880 881 .Build(); 882 } 883 884 llvm::SmallVector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr, 885 const Environment &Env) { 886 if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>( 887 getLocBehindPossiblePointer(*ObjectExpr, Env))) { 888 auto *Prop = Env.getValue(locForHasValue(*OptionalLoc)); 889 if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) { 890 if (Env.proves(HasValueVal->formula())) 891 return {}; 892 } 893 } 894 895 // Record that this unwrap is *not* provably safe. 896 // FIXME: include either the name of the optional (if applicable) or a source 897 // range of the access for easier interpretation of the result. 898 return {ObjectExpr->getBeginLoc()}; 899 } 900 901 auto buildDiagnoseMatchSwitch( 902 const UncheckedOptionalAccessModelOptions &Options) { 903 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 904 // lot of duplicated work (e.g. string comparisons), consider providing APIs 905 // that avoid it through memoization. 906 auto IgnorableOptional = ignorableOptional(Options); 907 return CFGMatchSwitchBuilder<const Environment, 908 llvm::SmallVector<SourceLocation>>() 909 // optional::value 910 .CaseOfCFGStmt<CXXMemberCallExpr>( 911 valueCall(IgnorableOptional), 912 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 913 const Environment &Env) { 914 return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env); 915 }) 916 917 // optional::operator*, optional::operator-> 918 .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional), 919 [](const CallExpr *E, 920 const MatchFinder::MatchResult &, 921 const Environment &Env) { 922 return diagnoseUnwrapCall(E->getArg(0), Env); 923 }) 924 .Build(); 925 } 926 927 } // namespace 928 929 ast_matchers::DeclarationMatcher 930 UncheckedOptionalAccessModel::optionalClassDecl() { 931 return cxxRecordDecl(optionalClass()); 932 } 933 934 UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx, 935 Environment &Env) 936 : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx), 937 TransferMatchSwitch(buildTransferMatchSwitch()) { 938 Env.getDataflowAnalysisContext().setSyntheticFieldCallback( 939 [&Ctx](QualType Ty) -> llvm::StringMap<QualType> { 940 const CXXRecordDecl *Optional = 941 getOptionalBaseClass(Ty->getAsCXXRecordDecl()); 942 if (Optional == nullptr) 943 return {}; 944 return {{"value", valueTypeFromOptionalDecl(*Optional)}, 945 {"has_value", Ctx.BoolTy}}; 946 }); 947 } 948 949 void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt, 950 NoopLattice &L, Environment &Env) { 951 LatticeTransferState State(L, Env); 952 TransferMatchSwitch(Elt, getASTContext(), State); 953 } 954 955 UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser( 956 UncheckedOptionalAccessModelOptions Options) 957 : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {} 958 959 } // namespace dataflow 960 } // namespace clang 961