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