Wei Yi Tee | 543af74 | 2022-06-01 06:52:24 -0700 | [diff] [blame] | 1 | // Part of the Crubit project, under the Apache License v2.0 with LLVM |
| 2 | // Exceptions. See /LICENSE for license information. |
| 3 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 4 | |
| 5 | #include "nullability_verification/pointer_nullability_analysis.h" |
| 6 | |
Wei Yi Tee | 543af74 | 2022-06-01 06:52:24 -0700 | [diff] [blame] | 7 | #include <string> |
| 8 | |
| 9 | #include "common/check.h" |
| 10 | #include "nullability_verification/pointer_nullability_lattice.h" |
| 11 | #include "nullability_verification/pointer_nullability_matchers.h" |
| 12 | #include "clang/AST/ASTContext.h" |
| 13 | #include "clang/AST/Expr.h" |
Wei Yi Tee | 1cd62af | 2022-06-09 00:56:46 -0700 | [diff] [blame] | 14 | #include "clang/AST/OperationKinds.h" |
| 15 | #include "clang/AST/Stmt.h" |
| 16 | #include "clang/AST/Type.h" |
Wei Yi Tee | 543af74 | 2022-06-01 06:52:24 -0700 | [diff] [blame] | 17 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
Wei Yi Tee | 543af74 | 2022-06-01 06:52:24 -0700 | [diff] [blame] | 18 | #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" |
| 19 | #include "clang/Analysis/FlowSensitive/MatchSwitch.h" |
| 20 | #include "clang/Analysis/FlowSensitive/Value.h" |
| 21 | #include "clang/Basic/LLVM.h" |
| 22 | |
| 23 | namespace clang { |
| 24 | namespace tidy { |
| 25 | namespace nullability { |
| 26 | |
| 27 | using ast_matchers::MatchFinder; |
| 28 | using dataflow::BoolValue; |
| 29 | using dataflow::Environment; |
| 30 | using dataflow::MatchSwitchBuilder; |
| 31 | using dataflow::PointerValue; |
| 32 | using dataflow::SkipPast; |
| 33 | using dataflow::TransferState; |
| 34 | using dataflow::Value; |
| 35 | |
| 36 | namespace { |
| 37 | |
Wei Yi Tee | c46666a | 2022-08-02 09:38:40 -0700 | [diff] [blame] | 38 | constexpr llvm::StringLiteral kNotNull = "is_notnull"; |
| 39 | |
Wei Yi Tee | 85d8737 | 2022-06-09 01:01:35 -0700 | [diff] [blame] | 40 | BoolValue& getPointerNotNullProperty( |
Wei Yi Tee | 1cd62af | 2022-06-09 00:56:46 -0700 | [diff] [blame] | 41 | const Expr* PointerExpr, TransferState<PointerNullabilityLattice>& State) { |
| 42 | auto* PointerVal = |
| 43 | cast<PointerValue>(State.Env.getValue(*PointerExpr, SkipPast::Reference)); |
Wei Yi Tee | c46666a | 2022-08-02 09:38:40 -0700 | [diff] [blame] | 44 | return *cast<BoolValue>(PointerVal->getProperty(kNotNull)); |
Wei Yi Tee | 1cd62af | 2022-06-09 00:56:46 -0700 | [diff] [blame] | 45 | } |
| 46 | |
Wei Yi Tee | c46666a | 2022-08-02 09:38:40 -0700 | [diff] [blame] | 47 | void initPointerNotNullProperty(const Expr* PointerExpr, |
| 48 | TransferState<PointerNullabilityLattice>& State, |
| 49 | BoolValue* NotNull = nullptr) { |
Wei Yi Tee | 543af74 | 2022-06-01 06:52:24 -0700 | [diff] [blame] | 50 | if (auto* PointerVal = cast_or_null<PointerValue>( |
Wei Yi Tee | f02698e | 2022-06-10 04:25:48 -0700 | [diff] [blame] | 51 | State.Env.getValue(*PointerExpr, SkipPast::Reference))) { |
Wei Yi Tee | c46666a | 2022-08-02 09:38:40 -0700 | [diff] [blame] | 52 | if (PointerVal->getProperty(kNotNull) == nullptr) { |
| 53 | NotNull = NotNull ? NotNull : &State.Env.makeAtomicBoolValue(); |
| 54 | PointerVal->setProperty(kNotNull, *NotNull); |
Wei Yi Tee | 543af74 | 2022-06-01 06:52:24 -0700 | [diff] [blame] | 55 | } |
| 56 | } |
| 57 | } |
| 58 | |
Wei Yi Tee | c46666a | 2022-08-02 09:38:40 -0700 | [diff] [blame] | 59 | void transferInitNotNullPointer( |
| 60 | const Expr* NotNullPointer, const MatchFinder::MatchResult&, |
| 61 | TransferState<PointerNullabilityLattice>& State) { |
| 62 | initPointerNotNullProperty(NotNullPointer, State, |
| 63 | &State.Env.getBoolLiteralValue(true)); |
| 64 | } |
| 65 | |
| 66 | void transferInitNullPointer(const Expr* NullPointer, |
| 67 | const MatchFinder::MatchResult& Result, |
| 68 | TransferState<PointerNullabilityLattice>& State) { |
| 69 | initPointerNotNullProperty(NullPointer, State, |
| 70 | &State.Env.getBoolLiteralValue(false)); |
| 71 | } |
| 72 | |
Wei Yi Tee | f02698e | 2022-06-10 04:25:48 -0700 | [diff] [blame] | 73 | void transferInitPointerVariableReference( |
Wei Yi Tee | cebc226 | 2022-06-10 04:27:57 -0700 | [diff] [blame] | 74 | const Expr* PointerExpr, const MatchFinder::MatchResult&, |
Wei Yi Tee | f02698e | 2022-06-10 04:25:48 -0700 | [diff] [blame] | 75 | TransferState<PointerNullabilityLattice>& State) { |
Wei Yi Tee | c46666a | 2022-08-02 09:38:40 -0700 | [diff] [blame] | 76 | initPointerNotNullProperty(PointerExpr, State); |
Wei Yi Tee | 1cd62af | 2022-06-09 00:56:46 -0700 | [diff] [blame] | 77 | } |
| 78 | |
Wei Yi Tee | f02698e | 2022-06-10 04:25:48 -0700 | [diff] [blame] | 79 | void transferPointerAccess(const Expr* PointerExpr, |
| 80 | TransferState<PointerNullabilityLattice>& State) { |
Wei Yi Tee | 85d8737 | 2022-06-09 01:01:35 -0700 | [diff] [blame] | 81 | auto& PointerNotNull = getPointerNotNullProperty(PointerExpr, State); |
| 82 | if (!State.Env.flowConditionImplies(PointerNotNull)) { |
Wei Yi Tee | 1cd62af | 2022-06-09 00:56:46 -0700 | [diff] [blame] | 83 | State.Lattice.addViolation(PointerExpr); |
Wei Yi Tee | 543af74 | 2022-06-01 06:52:24 -0700 | [diff] [blame] | 84 | } |
Wei Yi Tee | 543af74 | 2022-06-01 06:52:24 -0700 | [diff] [blame] | 85 | } |
| 86 | |
Wei Yi Tee | f02698e | 2022-06-10 04:25:48 -0700 | [diff] [blame] | 87 | void transferDereference(const UnaryOperator* UnaryOp, |
| 88 | const MatchFinder::MatchResult&, |
| 89 | TransferState<PointerNullabilityLattice>& State) { |
| 90 | transferPointerAccess(UnaryOp->getSubExpr(), State); |
| 91 | } |
| 92 | |
| 93 | void transferMemberExprInvolvingPointers( |
| 94 | const MemberExpr* MemberExpr, const MatchFinder::MatchResult&, |
| 95 | TransferState<PointerNullabilityLattice>& State) { |
| 96 | if (MemberExpr->isArrow()) { |
| 97 | // Base expr is a pointer, check that (->) access is safe |
| 98 | transferPointerAccess(MemberExpr->getBase(), State); |
| 99 | } |
| 100 | if (MemberExpr->getType()->isAnyPointerType()) { |
| 101 | // Accessed member is a pointer, initialise its nullability |
Wei Yi Tee | c46666a | 2022-08-02 09:38:40 -0700 | [diff] [blame] | 102 | initPointerNotNullProperty(MemberExpr, State); |
Wei Yi Tee | f02698e | 2022-06-10 04:25:48 -0700 | [diff] [blame] | 103 | } |
| 104 | } |
| 105 | |
Wei Yi Tee | 543af74 | 2022-06-01 06:52:24 -0700 | [diff] [blame] | 106 | void transferNullCheckComparison( |
Wei Yi Tee | 1cd62af | 2022-06-09 00:56:46 -0700 | [diff] [blame] | 107 | const BinaryOperator* BinaryOp, const MatchFinder::MatchResult& result, |
Wei Yi Tee | 543af74 | 2022-06-01 06:52:24 -0700 | [diff] [blame] | 108 | TransferState<PointerNullabilityLattice>& State) { |
Wei Yi Tee | 1cd62af | 2022-06-09 00:56:46 -0700 | [diff] [blame] | 109 | // Boolean representing the comparison between the two pointer values, |
| 110 | // automatically created by the dataflow framework |
| 111 | auto& PointerComparison = |
| 112 | *cast<BoolValue>(State.Env.getValue(*BinaryOp, SkipPast::None)); |
Wei Yi Tee | 543af74 | 2022-06-01 06:52:24 -0700 | [diff] [blame] | 113 | |
Wei Yi Tee | 1cd62af | 2022-06-09 00:56:46 -0700 | [diff] [blame] | 114 | CHECK(BinaryOp->getOpcode() == BO_EQ || BinaryOp->getOpcode() == BO_NE); |
| 115 | auto& PointerEQ = BinaryOp->getOpcode() == BO_EQ |
| 116 | ? PointerComparison |
| 117 | : State.Env.makeNot(PointerComparison); |
| 118 | auto& PointerNE = BinaryOp->getOpcode() == BO_EQ |
| 119 | ? State.Env.makeNot(PointerComparison) |
| 120 | : PointerComparison; |
| 121 | |
Wei Yi Tee | 85d8737 | 2022-06-09 01:01:35 -0700 | [diff] [blame] | 122 | auto& LHSNotNull = getPointerNotNullProperty(BinaryOp->getLHS(), State); |
| 123 | auto& RHSNotNull = getPointerNotNullProperty(BinaryOp->getRHS(), State); |
Wei Yi Tee | 1cd62af | 2022-06-09 00:56:46 -0700 | [diff] [blame] | 124 | |
| 125 | // !LHS && !RHS => LHS == RHS |
| 126 | State.Env.addToFlowCondition(State.Env.makeImplication( |
Wei Yi Tee | 85d8737 | 2022-06-09 01:01:35 -0700 | [diff] [blame] | 127 | State.Env.makeAnd(State.Env.makeNot(LHSNotNull), |
| 128 | State.Env.makeNot(RHSNotNull)), |
Wei Yi Tee | 1cd62af | 2022-06-09 00:56:46 -0700 | [diff] [blame] | 129 | PointerEQ)); |
| 130 | // !LHS && RHS => LHS != RHS |
| 131 | State.Env.addToFlowCondition(State.Env.makeImplication( |
Wei Yi Tee | 85d8737 | 2022-06-09 01:01:35 -0700 | [diff] [blame] | 132 | State.Env.makeAnd(State.Env.makeNot(LHSNotNull), RHSNotNull), PointerNE)); |
Wei Yi Tee | 1cd62af | 2022-06-09 00:56:46 -0700 | [diff] [blame] | 133 | // LHS && !RHS => LHS != RHS |
| 134 | State.Env.addToFlowCondition(State.Env.makeImplication( |
Wei Yi Tee | 85d8737 | 2022-06-09 01:01:35 -0700 | [diff] [blame] | 135 | State.Env.makeAnd(LHSNotNull, State.Env.makeNot(RHSNotNull)), PointerNE)); |
Wei Yi Tee | 543af74 | 2022-06-01 06:52:24 -0700 | [diff] [blame] | 136 | } |
| 137 | |
| 138 | void transferNullCheckImplicitCastPtrToBool( |
| 139 | const Expr* CastExpr, const MatchFinder::MatchResult&, |
| 140 | TransferState<PointerNullabilityLattice>& State) { |
Wei Yi Tee | c46666a | 2022-08-02 09:38:40 -0700 | [diff] [blame] | 141 | auto& PointerNotNull = |
| 142 | getPointerNotNullProperty(CastExpr->IgnoreImplicit(), State); |
| 143 | auto& CastExprLoc = State.Env.createStorageLocation(*CastExpr); |
| 144 | State.Env.setValue(CastExprLoc, PointerNotNull); |
| 145 | State.Env.setStorageLocation(*CastExpr, CastExprLoc); |
Wei Yi Tee | 543af74 | 2022-06-01 06:52:24 -0700 | [diff] [blame] | 146 | } |
| 147 | |
| 148 | auto buildTransferer() { |
| 149 | return MatchSwitchBuilder<TransferState<PointerNullabilityLattice>>() |
Wei Yi Tee | 1cd62af | 2022-06-09 00:56:46 -0700 | [diff] [blame] | 150 | // Handles initialization of the null states of pointers |
Wei Yi Tee | 85d8737 | 2022-06-09 01:01:35 -0700 | [diff] [blame] | 151 | .CaseOf<Expr>(isPointerVariableReference(), |
Wei Yi Tee | f02698e | 2022-06-10 04:25:48 -0700 | [diff] [blame] | 152 | transferInitPointerVariableReference) |
Wei Yi Tee | c46666a | 2022-08-02 09:38:40 -0700 | [diff] [blame] | 153 | .CaseOf<Expr>(isCXXThisExpr(), transferInitNotNullPointer) |
| 154 | .CaseOf<Expr>(isAddrOf(), transferInitNotNullPointer) |
| 155 | .CaseOf<Expr>(isNullPointerLiteral(), transferInitNullPointer) |
Wei Yi Tee | f02698e | 2022-06-10 04:25:48 -0700 | [diff] [blame] | 156 | // Handles initialization of null states of member pointers and safety of |
| 157 | // member access (->) on pointers |
| 158 | .CaseOf<MemberExpr>(isMemberExprInvolvingPointers(), |
| 159 | transferMemberExprInvolvingPointers) |
Wei Yi Tee | 1cd62af | 2022-06-09 00:56:46 -0700 | [diff] [blame] | 160 | // Handles pointer dereferencing (*ptr) |
Wei Yi Tee | 543af74 | 2022-06-01 06:52:24 -0700 | [diff] [blame] | 161 | .CaseOf<UnaryOperator>(isPointerDereference(), transferDereference) |
Wei Yi Tee | 1cd62af | 2022-06-09 00:56:46 -0700 | [diff] [blame] | 162 | // Handles comparison between 2 pointers |
| 163 | .CaseOf<BinaryOperator>(isPointerCheckBinOp(), |
| 164 | transferNullCheckComparison) |
| 165 | // Handles checking of pointer as boolean |
| 166 | .CaseOf<Expr>(isImplicitCastPointerToBool(), |
Wei Yi Tee | 543af74 | 2022-06-01 06:52:24 -0700 | [diff] [blame] | 167 | transferNullCheckImplicitCastPtrToBool) |
| 168 | .Build(); |
| 169 | } |
| 170 | } // namespace |
| 171 | |
| 172 | PointerNullabilityAnalysis::PointerNullabilityAnalysis(ASTContext& Context) |
| 173 | : DataflowAnalysis<PointerNullabilityAnalysis, PointerNullabilityLattice>( |
| 174 | Context), |
| 175 | Transferer(buildTransferer()) {} |
| 176 | |
| 177 | void PointerNullabilityAnalysis::transfer(const Stmt* Stmt, |
| 178 | PointerNullabilityLattice& Lattice, |
| 179 | Environment& Env) { |
| 180 | TransferState<PointerNullabilityLattice> State(Lattice, Env); |
| 181 | Transferer(*Stmt, getASTContext(), State); |
| 182 | } |
| 183 | |
| 184 | bool PointerNullabilityAnalysis::merge(QualType Type, const Value& Val1, |
| 185 | const Environment& Env1, |
| 186 | const Value& Val2, |
| 187 | const Environment& Env2, |
| 188 | Value& MergedVal, |
| 189 | Environment& MergedEnv) { |
| 190 | return false; |
| 191 | } |
| 192 | } // namespace nullability |
| 193 | } // namespace tidy |
| 194 | } // namespace clang |