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