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(const FunctionDecl &F, const Expr &E, 516 const MatchFinder::MatchResult &MatchRes, 517 LatticeTransferState &State) { 518 assert(F.getTemplateSpecializationArgs() != nullptr); 519 assert(F.getTemplateSpecializationArgs()->size() > 0); 520 521 const int TemplateParamOptionalWrappersCount = 522 countOptionalWrappers(*MatchRes.Context, F.getTemplateSpecializationArgs() 523 ->get(0) 524 .getAsType() 525 .getNonReferenceType()); 526 const int ArgTypeOptionalWrappersCount = countOptionalWrappers( 527 *MatchRes.Context, E.getType().getNonReferenceType()); 528 529 // Check if this is a constructor/assignment call for `optional<T>` with 530 // argument of type `U` such that `T` is constructible from `U`. 531 if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount) 532 return State.Env.getBoolLiteralValue(true); 533 534 // This is a constructor/assignment call for `optional<T>` with argument of 535 // type `optional<U>` such that `T` is constructible from `U`. 536 auto *Loc = State.Env.get<RecordStorageLocation>(E); 537 if (auto *HasValueVal = getHasValue(State.Env, Loc)) 538 return *HasValueVal; 539 return State.Env.makeAtomicBoolValue(); 540 } 541 542 void transferValueOrConversionConstructor( 543 const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes, 544 LatticeTransferState &State) { 545 assert(E->getNumArgs() > 0); 546 547 constructOptionalValue(*E, State.Env, 548 valueOrConversionHasValue(*E->getConstructor(), 549 *E->getArg(0), MatchRes, 550 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(E, 570 valueOrConversionHasValue(*E->getDirectCallee(), 571 *E->getArg(1), MatchRes, State), 572 State); 573 } 574 575 void transferNulloptAssignment(const CXXOperatorCallExpr *E, 576 const MatchFinder::MatchResult &, 577 LatticeTransferState &State) { 578 transferAssignment(E, State.Env.getBoolLiteralValue(false), State); 579 } 580 581 void transferSwap(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2, 582 Environment &Env) { 583 // We account for cases where one or both of the optionals are not modeled, 584 // either lacking associated storage locations, or lacking values associated 585 // to such storage locations. 586 587 if (Loc1 == nullptr) { 588 if (Loc2 != nullptr) 589 createOptionalValue(*Loc2, Env.makeAtomicBoolValue(), Env); 590 return; 591 } 592 if (Loc2 == nullptr) { 593 createOptionalValue(*Loc1, Env.makeAtomicBoolValue(), Env); 594 return; 595 } 596 597 // Both expressions have locations, though they may not have corresponding 598 // values. In that case, we create a fresh value at this point. Note that if 599 // two branches both do this, they will not share the value, but it at least 600 // allows for local reasoning about the value. To avoid the above, we would 601 // need *lazy* value allocation. 602 // FIXME: allocate values lazily, instead of just creating a fresh value. 603 BoolValue *BoolVal1 = getHasValue(Env, Loc1); 604 if (BoolVal1 == nullptr) 605 BoolVal1 = &Env.makeAtomicBoolValue(); 606 607 BoolValue *BoolVal2 = getHasValue(Env, Loc2); 608 if (BoolVal2 == nullptr) 609 BoolVal2 = &Env.makeAtomicBoolValue(); 610 611 createOptionalValue(*Loc1, *BoolVal2, Env); 612 createOptionalValue(*Loc2, *BoolVal1, Env); 613 } 614 615 void transferSwapCall(const CXXMemberCallExpr *E, 616 const MatchFinder::MatchResult &, 617 LatticeTransferState &State) { 618 assert(E->getNumArgs() == 1); 619 auto *OtherLoc = State.Env.get<RecordStorageLocation>(*E->getArg(0)); 620 transferSwap(getImplicitObjectLocation(*E, State.Env), OtherLoc, State.Env); 621 } 622 623 void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &, 624 LatticeTransferState &State) { 625 assert(E->getNumArgs() == 2); 626 auto *Arg0Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0)); 627 auto *Arg1Loc = State.Env.get<RecordStorageLocation>(*E->getArg(1)); 628 transferSwap(Arg0Loc, Arg1Loc, State.Env); 629 } 630 631 void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &, 632 LatticeTransferState &State) { 633 assert(E->getNumArgs() == 1); 634 635 if (auto *Loc = State.Env.getStorageLocation(*E->getArg(0))) 636 State.Env.setStorageLocation(*E, *Loc); 637 } 638 639 const Formula &evaluateEquality(Arena &A, const Formula &EqVal, 640 const Formula &LHS, const Formula &RHS) { 641 // Logically, an optional<T> object is composed of two values - a `has_value` 642 // bit and a value of type T. Equality of optional objects compares both 643 // values. Therefore, merely comparing the `has_value` bits isn't sufficient: 644 // when two optional objects are engaged, the equality of their respective 645 // values of type T matters. Since we only track the `has_value` bits, we 646 // can't make any conclusions about equality when we know that two optional 647 // objects are engaged. 648 // 649 // We express this as two facts about the equality: 650 // a) EqVal => (LHS & RHS) v (!RHS & !LHS) 651 // If they are equal, then either both are set or both are unset. 652 // b) (!LHS & !RHS) => EqVal 653 // If neither is set, then they are equal. 654 // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula. 655 return A.makeAnd( 656 A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS), 657 A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))), 658 A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS))); 659 } 660 661 void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr, 662 const MatchFinder::MatchResult &, 663 LatticeTransferState &State) { 664 Environment &Env = State.Env; 665 auto &A = Env.arena(); 666 auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 667 auto *Arg0Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(0)); 668 if (auto *LHasVal = getHasValue(Env, Arg0Loc)) { 669 auto *Arg1Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(1)); 670 if (auto *RHasVal = getHasValue(Env, Arg1Loc)) { 671 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 672 CmpValue = &A.makeNot(*CmpValue); 673 Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(), 674 RHasVal->formula())); 675 } 676 } 677 } 678 679 void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr, 680 const clang::Expr *E, Environment &Env) { 681 auto &A = Env.arena(); 682 auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 683 auto *Loc = Env.get<RecordStorageLocation>(*E); 684 if (auto *HasVal = getHasValue(Env, Loc)) { 685 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 686 CmpValue = &A.makeNot(*CmpValue); 687 Env.assume( 688 evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true))); 689 } 690 } 691 692 void transferOptionalAndNulloptCmp(const clang::CXXOperatorCallExpr *CmpExpr, 693 const clang::Expr *E, Environment &Env) { 694 auto &A = Env.arena(); 695 auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 696 auto *Loc = Env.get<RecordStorageLocation>(*E); 697 if (auto *HasVal = getHasValue(Env, Loc)) { 698 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 699 CmpValue = &A.makeNot(*CmpValue); 700 Env.assume(evaluateEquality(A, *CmpValue, HasVal->formula(), 701 A.makeLiteral(false))); 702 } 703 } 704 705 std::optional<StatementMatcher> 706 ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) { 707 if (Options.IgnoreSmartPointerDereference) { 708 auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr( 709 anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")), 710 unless(hasArgument(0, expr(hasOptionalType())))))); 711 return expr( 712 anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse)))); 713 } 714 return std::nullopt; 715 } 716 717 StatementMatcher 718 valueCall(const std::optional<StatementMatcher> &IgnorableOptional) { 719 return isOptionalMemberCallWithNameMatcher(hasName("value"), 720 IgnorableOptional); 721 } 722 723 StatementMatcher 724 valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) { 725 return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional), 726 isOptionalOperatorCallWithName("->", IgnorableOptional))); 727 } 728 729 auto buildTransferMatchSwitch() { 730 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 731 // lot of duplicated work (e.g. string comparisons), consider providing APIs 732 // that avoid it through memoization. 733 return CFGMatchSwitchBuilder<LatticeTransferState>() 734 // make_optional 735 .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall) 736 737 // optional::optional (in place) 738 .CaseOfCFGStmt<CXXConstructExpr>( 739 isOptionalInPlaceConstructor(), 740 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 741 LatticeTransferState &State) { 742 constructOptionalValue(*E, State.Env, 743 State.Env.getBoolLiteralValue(true)); 744 }) 745 // optional::optional(nullopt_t) 746 .CaseOfCFGStmt<CXXConstructExpr>( 747 isOptionalNulloptConstructor(), 748 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 749 LatticeTransferState &State) { 750 constructOptionalValue(*E, State.Env, 751 State.Env.getBoolLiteralValue(false)); 752 }) 753 // optional::optional (value/conversion) 754 .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(), 755 transferValueOrConversionConstructor) 756 757 // optional::operator= 758 .CaseOfCFGStmt<CXXOperatorCallExpr>( 759 isOptionalValueOrConversionAssignment(), 760 transferValueOrConversionAssignment) 761 .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(), 762 transferNulloptAssignment) 763 764 // optional::value 765 .CaseOfCFGStmt<CXXMemberCallExpr>( 766 valueCall(std::nullopt), 767 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 768 LatticeTransferState &State) { 769 transferUnwrapCall(E, E->getImplicitObjectArgument(), State); 770 }) 771 772 // optional::operator* 773 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"), 774 [](const CallExpr *E, 775 const MatchFinder::MatchResult &, 776 LatticeTransferState &State) { 777 transferUnwrapCall(E, E->getArg(0), State); 778 }) 779 780 // optional::operator-> 781 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"), 782 [](const CallExpr *E, 783 const MatchFinder::MatchResult &, 784 LatticeTransferState &State) { 785 transferArrowOpCall(E, E->getArg(0), State); 786 }) 787 788 // optional::has_value, optional::hasValue 789 // Of the supported optionals only folly::Optional uses hasValue, but this 790 // will also pass for other types 791 .CaseOfCFGStmt<CXXMemberCallExpr>( 792 isOptionalMemberCallWithNameMatcher( 793 hasAnyName("has_value", "hasValue")), 794 transferOptionalHasValueCall) 795 796 // optional::operator bool 797 .CaseOfCFGStmt<CXXMemberCallExpr>( 798 isOptionalMemberCallWithNameMatcher(hasName("operator bool")), 799 transferOptionalHasValueCall) 800 801 // optional::emplace 802 .CaseOfCFGStmt<CXXMemberCallExpr>( 803 isOptionalMemberCallWithNameMatcher(hasName("emplace")), 804 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 805 LatticeTransferState &State) { 806 if (RecordStorageLocation *Loc = 807 getImplicitObjectLocation(*E, State.Env)) { 808 createOptionalValue(*Loc, State.Env.getBoolLiteralValue(true), 809 State.Env); 810 } 811 }) 812 813 // optional::reset 814 .CaseOfCFGStmt<CXXMemberCallExpr>( 815 isOptionalMemberCallWithNameMatcher(hasName("reset")), 816 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 817 LatticeTransferState &State) { 818 if (RecordStorageLocation *Loc = 819 getImplicitObjectLocation(*E, State.Env)) { 820 createOptionalValue(*Loc, State.Env.getBoolLiteralValue(false), 821 State.Env); 822 } 823 }) 824 825 // optional::swap 826 .CaseOfCFGStmt<CXXMemberCallExpr>( 827 isOptionalMemberCallWithNameMatcher(hasName("swap")), 828 transferSwapCall) 829 830 // std::swap 831 .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall) 832 833 // std::forward 834 .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall) 835 836 // opt.value_or("").empty() 837 .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(), 838 transferValueOrStringEmptyCall) 839 840 // opt.value_or(X) != X 841 .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX) 842 843 // Comparisons (==, !=): 844 .CaseOfCFGStmt<CXXOperatorCallExpr>( 845 isComparisonOperatorCall(hasOptionalType(), hasOptionalType()), 846 transferOptionalAndOptionalCmp) 847 .CaseOfCFGStmt<CXXOperatorCallExpr>( 848 isComparisonOperatorCall(hasOptionalType(), hasNulloptType()), 849 [](const clang::CXXOperatorCallExpr *Cmp, 850 const MatchFinder::MatchResult &, LatticeTransferState &State) { 851 transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(0), State.Env); 852 }) 853 .CaseOfCFGStmt<CXXOperatorCallExpr>( 854 isComparisonOperatorCall(hasNulloptType(), hasOptionalType()), 855 [](const clang::CXXOperatorCallExpr *Cmp, 856 const MatchFinder::MatchResult &, LatticeTransferState &State) { 857 transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(1), State.Env); 858 }) 859 .CaseOfCFGStmt<CXXOperatorCallExpr>( 860 isComparisonOperatorCall( 861 hasOptionalType(), 862 unless(anyOf(hasOptionalType(), hasNulloptType()))), 863 [](const clang::CXXOperatorCallExpr *Cmp, 864 const MatchFinder::MatchResult &, LatticeTransferState &State) { 865 transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env); 866 }) 867 .CaseOfCFGStmt<CXXOperatorCallExpr>( 868 isComparisonOperatorCall( 869 unless(anyOf(hasOptionalType(), hasNulloptType())), 870 hasOptionalType()), 871 [](const clang::CXXOperatorCallExpr *Cmp, 872 const MatchFinder::MatchResult &, LatticeTransferState &State) { 873 transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env); 874 }) 875 876 // returns optional 877 .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(), 878 transferCallReturningOptional) 879 880 .Build(); 881 } 882 883 llvm::SmallVector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr, 884 const Environment &Env) { 885 if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>( 886 getLocBehindPossiblePointer(*ObjectExpr, Env))) { 887 auto *Prop = Env.getValue(locForHasValue(*OptionalLoc)); 888 if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) { 889 if (Env.proves(HasValueVal->formula())) 890 return {}; 891 } 892 } 893 894 // Record that this unwrap is *not* provably safe. 895 // FIXME: include either the name of the optional (if applicable) or a source 896 // range of the access for easier interpretation of the result. 897 return {ObjectExpr->getBeginLoc()}; 898 } 899 900 auto buildDiagnoseMatchSwitch( 901 const UncheckedOptionalAccessModelOptions &Options) { 902 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 903 // lot of duplicated work (e.g. string comparisons), consider providing APIs 904 // that avoid it through memoization. 905 auto IgnorableOptional = ignorableOptional(Options); 906 return CFGMatchSwitchBuilder<const Environment, 907 llvm::SmallVector<SourceLocation>>() 908 // optional::value 909 .CaseOfCFGStmt<CXXMemberCallExpr>( 910 valueCall(IgnorableOptional), 911 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 912 const Environment &Env) { 913 return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env); 914 }) 915 916 // optional::operator*, optional::operator-> 917 .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional), 918 [](const CallExpr *E, 919 const MatchFinder::MatchResult &, 920 const Environment &Env) { 921 return diagnoseUnwrapCall(E->getArg(0), Env); 922 }) 923 .Build(); 924 } 925 926 } // namespace 927 928 ast_matchers::DeclarationMatcher 929 UncheckedOptionalAccessModel::optionalClassDecl() { 930 return cxxRecordDecl(optionalClass()); 931 } 932 933 UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx, 934 Environment &Env) 935 : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx), 936 TransferMatchSwitch(buildTransferMatchSwitch()) { 937 Env.getDataflowAnalysisContext().setSyntheticFieldCallback( 938 [&Ctx](QualType Ty) -> llvm::StringMap<QualType> { 939 const CXXRecordDecl *Optional = 940 getOptionalBaseClass(Ty->getAsCXXRecordDecl()); 941 if (Optional == nullptr) 942 return {}; 943 return {{"value", valueTypeFromOptionalDecl(*Optional)}, 944 {"has_value", Ctx.BoolTy}}; 945 }); 946 } 947 948 void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt, 949 NoopLattice &L, Environment &Env) { 950 LatticeTransferState State(L, Env); 951 TransferMatchSwitch(Elt, getASTContext(), State); 952 } 953 954 UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser( 955 UncheckedOptionalAccessModelOptions Options) 956 : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {} 957 958 } // namespace dataflow 959 } // namespace clang 960