1//== TrustNonnullChecker.cpp --------- API nullability modeling -*- C++ -*--==// 2//
| 1//== TrustNonnullChecker.cpp --------- API nullability modeling -*- C++ -*--==// 2//
|
3// The LLVM Compiler Infrastructure
| 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
|
4//
| 6//
|
5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7//
| |
8//===----------------------------------------------------------------------===// 9// 10// This checker adds nullability-related assumptions: 11// 12// 1. Methods annotated with _Nonnull 13// which come from system headers actually return a non-null pointer. 14// 15// 2. NSDictionary key is non-null after the keyword subscript operation 16// on read if and only if the resulting expression is non-null. 17// 18// 3. NSMutableDictionary index is non-null after a write operation. 19// 20//===----------------------------------------------------------------------===// 21 22#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 23#include "clang/Analysis/SelectorExtras.h" 24#include "clang/StaticAnalyzer/Core/Checker.h" 25#include "clang/StaticAnalyzer/Core/CheckerManager.h" 26#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 27#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" 28#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 29 30using namespace clang; 31using namespace ento; 32 33/// Records implications between symbols. 34/// The semantics is: 35/// (antecedent != 0) => (consequent != 0) 36/// These implications are then read during the evaluation of the assumption, 37/// and the appropriate antecedents are applied. 38REGISTER_MAP_WITH_PROGRAMSTATE(NonNullImplicationMap, SymbolRef, SymbolRef) 39 40/// The semantics is: 41/// (antecedent == 0) => (consequent == 0) 42REGISTER_MAP_WITH_PROGRAMSTATE(NullImplicationMap, SymbolRef, SymbolRef) 43 44namespace { 45 46class TrustNonnullChecker : public Checker<check::PostCall, 47 check::PostObjCMessage, 48 check::DeadSymbols, 49 eval::Assume> { 50 // Do not try to iterate over symbols with higher complexity. 51 static unsigned constexpr ComplexityThreshold = 10; 52 Selector ObjectForKeyedSubscriptSel; 53 Selector ObjectForKeySel; 54 Selector SetObjectForKeyedSubscriptSel; 55 Selector SetObjectForKeySel; 56 57public: 58 TrustNonnullChecker(ASTContext &Ctx) 59 : ObjectForKeyedSubscriptSel( 60 getKeywordSelector(Ctx, "objectForKeyedSubscript")), 61 ObjectForKeySel(getKeywordSelector(Ctx, "objectForKey")), 62 SetObjectForKeyedSubscriptSel( 63 getKeywordSelector(Ctx, "setObject", "forKeyedSubscript")), 64 SetObjectForKeySel(getKeywordSelector(Ctx, "setObject", "forKey")) {} 65 66 ProgramStateRef evalAssume(ProgramStateRef State, 67 SVal Cond, 68 bool Assumption) const { 69 const SymbolRef CondS = Cond.getAsSymbol(); 70 if (!CondS || CondS->computeComplexity() > ComplexityThreshold) 71 return State; 72 73 for (auto B=CondS->symbol_begin(), E=CondS->symbol_end(); B != E; ++B) { 74 const SymbolRef Antecedent = *B; 75 State = addImplication(Antecedent, State, true); 76 State = addImplication(Antecedent, State, false); 77 } 78 79 return State; 80 } 81 82 void checkPostCall(const CallEvent &Call, CheckerContext &C) const { 83 // Only trust annotations for system headers for non-protocols. 84 if (!Call.isInSystemHeader()) 85 return; 86 87 ProgramStateRef State = C.getState(); 88 89 if (isNonNullPtr(Call, C)) 90 if (auto L = Call.getReturnValue().getAs<Loc>())
| 7//===----------------------------------------------------------------------===// 8// 9// This checker adds nullability-related assumptions: 10// 11// 1. Methods annotated with _Nonnull 12// which come from system headers actually return a non-null pointer. 13// 14// 2. NSDictionary key is non-null after the keyword subscript operation 15// on read if and only if the resulting expression is non-null. 16// 17// 3. NSMutableDictionary index is non-null after a write operation. 18// 19//===----------------------------------------------------------------------===// 20 21#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 22#include "clang/Analysis/SelectorExtras.h" 23#include "clang/StaticAnalyzer/Core/Checker.h" 24#include "clang/StaticAnalyzer/Core/CheckerManager.h" 25#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 26#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" 27#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 28 29using namespace clang; 30using namespace ento; 31 32/// Records implications between symbols. 33/// The semantics is: 34/// (antecedent != 0) => (consequent != 0) 35/// These implications are then read during the evaluation of the assumption, 36/// and the appropriate antecedents are applied. 37REGISTER_MAP_WITH_PROGRAMSTATE(NonNullImplicationMap, SymbolRef, SymbolRef) 38 39/// The semantics is: 40/// (antecedent == 0) => (consequent == 0) 41REGISTER_MAP_WITH_PROGRAMSTATE(NullImplicationMap, SymbolRef, SymbolRef) 42 43namespace { 44 45class TrustNonnullChecker : public Checker<check::PostCall, 46 check::PostObjCMessage, 47 check::DeadSymbols, 48 eval::Assume> { 49 // Do not try to iterate over symbols with higher complexity. 50 static unsigned constexpr ComplexityThreshold = 10; 51 Selector ObjectForKeyedSubscriptSel; 52 Selector ObjectForKeySel; 53 Selector SetObjectForKeyedSubscriptSel; 54 Selector SetObjectForKeySel; 55 56public: 57 TrustNonnullChecker(ASTContext &Ctx) 58 : ObjectForKeyedSubscriptSel( 59 getKeywordSelector(Ctx, "objectForKeyedSubscript")), 60 ObjectForKeySel(getKeywordSelector(Ctx, "objectForKey")), 61 SetObjectForKeyedSubscriptSel( 62 getKeywordSelector(Ctx, "setObject", "forKeyedSubscript")), 63 SetObjectForKeySel(getKeywordSelector(Ctx, "setObject", "forKey")) {} 64 65 ProgramStateRef evalAssume(ProgramStateRef State, 66 SVal Cond, 67 bool Assumption) const { 68 const SymbolRef CondS = Cond.getAsSymbol(); 69 if (!CondS || CondS->computeComplexity() > ComplexityThreshold) 70 return State; 71 72 for (auto B=CondS->symbol_begin(), E=CondS->symbol_end(); B != E; ++B) { 73 const SymbolRef Antecedent = *B; 74 State = addImplication(Antecedent, State, true); 75 State = addImplication(Antecedent, State, false); 76 } 77 78 return State; 79 } 80 81 void checkPostCall(const CallEvent &Call, CheckerContext &C) const { 82 // Only trust annotations for system headers for non-protocols. 83 if (!Call.isInSystemHeader()) 84 return; 85 86 ProgramStateRef State = C.getState(); 87 88 if (isNonNullPtr(Call, C)) 89 if (auto L = Call.getReturnValue().getAs<Loc>())
|
91 State = State->assume(*L, /*Assumption=*/true);
| 90 State = State->assume(*L, /*assumption=*/true);
|
92 93 C.addTransition(State); 94 } 95 96 void checkPostObjCMessage(const ObjCMethodCall &Msg, 97 CheckerContext &C) const { 98 const ObjCInterfaceDecl *ID = Msg.getReceiverInterface(); 99 if (!ID) 100 return; 101 102 ProgramStateRef State = C.getState(); 103 104 // Index to setter for NSMutableDictionary is assumed to be non-null, 105 // as an exception is thrown otherwise. 106 if (interfaceHasSuperclass(ID, "NSMutableDictionary") && 107 (Msg.getSelector() == SetObjectForKeyedSubscriptSel || 108 Msg.getSelector() == SetObjectForKeySel)) { 109 if (auto L = Msg.getArgSVal(1).getAs<Loc>())
| 91 92 C.addTransition(State); 93 } 94 95 void checkPostObjCMessage(const ObjCMethodCall &Msg, 96 CheckerContext &C) const { 97 const ObjCInterfaceDecl *ID = Msg.getReceiverInterface(); 98 if (!ID) 99 return; 100 101 ProgramStateRef State = C.getState(); 102 103 // Index to setter for NSMutableDictionary is assumed to be non-null, 104 // as an exception is thrown otherwise. 105 if (interfaceHasSuperclass(ID, "NSMutableDictionary") && 106 (Msg.getSelector() == SetObjectForKeyedSubscriptSel || 107 Msg.getSelector() == SetObjectForKeySel)) { 108 if (auto L = Msg.getArgSVal(1).getAs<Loc>())
|
110 State = State->assume(*L, /*Assumption=*/true);
| 109 State = State->assume(*L, /*assumption=*/true);
|
111 } 112 113 // Record an implication: index is non-null if the output is non-null. 114 if (interfaceHasSuperclass(ID, "NSDictionary") && 115 (Msg.getSelector() == ObjectForKeyedSubscriptSel || 116 Msg.getSelector() == ObjectForKeySel)) { 117 SymbolRef ArgS = Msg.getArgSVal(0).getAsSymbol(); 118 SymbolRef RetS = Msg.getReturnValue().getAsSymbol(); 119 120 if (ArgS && RetS) { 121 // Emulate an implication: the argument is non-null if 122 // the return value is non-null. 123 State = State->set<NonNullImplicationMap>(RetS, ArgS); 124 125 // Conversely, when the argument is null, the return value 126 // is definitely null. 127 State = State->set<NullImplicationMap>(ArgS, RetS); 128 } 129 } 130 131 C.addTransition(State); 132 } 133 134 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const { 135 ProgramStateRef State = C.getState(); 136 137 State = dropDeadFromGDM<NullImplicationMap>(SymReaper, State); 138 State = dropDeadFromGDM<NonNullImplicationMap>(SymReaper, State); 139 140 C.addTransition(State); 141 } 142 143private: 144 145 /// \returns State with GDM \p MapName where all dead symbols were 146 // removed. 147 template <typename MapName> 148 ProgramStateRef dropDeadFromGDM(SymbolReaper &SymReaper, 149 ProgramStateRef State) const { 150 for (const std::pair<SymbolRef, SymbolRef> &P : State->get<MapName>()) 151 if (!SymReaper.isLive(P.first) || !SymReaper.isLive(P.second)) 152 State = State->remove<MapName>(P.first); 153 return State; 154 } 155 156 /// \returns Whether we trust the result of the method call to be 157 /// a non-null pointer. 158 bool isNonNullPtr(const CallEvent &Call, CheckerContext &C) const { 159 QualType ExprRetType = Call.getResultType(); 160 if (!ExprRetType->isAnyPointerType()) 161 return false; 162 163 if (getNullabilityAnnotation(ExprRetType) == Nullability::Nonnull) 164 return true; 165 166 // The logic for ObjC instance method calls is more complicated, 167 // as the return value is nil when the receiver is nil. 168 if (!isa<ObjCMethodCall>(&Call)) 169 return false; 170 171 const auto *MCall = cast<ObjCMethodCall>(&Call); 172 const ObjCMethodDecl *MD = MCall->getDecl(); 173 174 // Distrust protocols. 175 if (isa<ObjCProtocolDecl>(MD->getDeclContext())) 176 return false; 177 178 QualType DeclRetType = MD->getReturnType(); 179 if (getNullabilityAnnotation(DeclRetType) != Nullability::Nonnull) 180 return false; 181 182 // For class messages it is sufficient for the declaration to be 183 // annotated _Nonnull. 184 if (!MCall->isInstanceMessage()) 185 return true; 186 187 // Alternatively, the analyzer could know that the receiver is not null. 188 SVal Receiver = MCall->getReceiverSVal(); 189 ConditionTruthVal TV = C.getState()->isNonNull(Receiver); 190 if (TV.isConstrainedTrue()) 191 return true; 192 193 return false; 194 } 195 196 /// \return Whether \p ID has a superclass by the name \p ClassName. 197 bool interfaceHasSuperclass(const ObjCInterfaceDecl *ID, 198 StringRef ClassName) const { 199 if (ID->getIdentifier()->getName() == ClassName) 200 return true; 201 202 if (const ObjCInterfaceDecl *Super = ID->getSuperClass()) 203 return interfaceHasSuperclass(Super, ClassName); 204 205 return false; 206 } 207 208 209 /// \return a state with an optional implication added (if exists) 210 /// from a map of recorded implications. 211 /// If \p Negated is true, checks NullImplicationMap, and assumes 212 /// the negation of \p Antecedent. 213 /// Checks NonNullImplicationMap and assumes \p Antecedent otherwise. 214 ProgramStateRef addImplication(SymbolRef Antecedent, 215 ProgramStateRef InputState, 216 bool Negated) const { 217 if (!InputState) 218 return nullptr; 219 SValBuilder &SVB = InputState->getStateManager().getSValBuilder(); 220 const SymbolRef *Consequent = 221 Negated ? InputState->get<NonNullImplicationMap>(Antecedent) 222 : InputState->get<NullImplicationMap>(Antecedent); 223 if (!Consequent) 224 return InputState; 225 226 SVal AntecedentV = SVB.makeSymbolVal(Antecedent); 227 ProgramStateRef State = InputState; 228 229 if ((Negated && InputState->isNonNull(AntecedentV).isConstrainedTrue()) 230 || (!Negated && InputState->isNull(AntecedentV).isConstrainedTrue())) { 231 SVal ConsequentS = SVB.makeSymbolVal(*Consequent); 232 State = InputState->assume(ConsequentS.castAs<DefinedSVal>(), Negated); 233 if (!State) 234 return nullptr; 235 236 // Drop implications from the map. 237 if (Negated) { 238 State = State->remove<NonNullImplicationMap>(Antecedent); 239 State = State->remove<NullImplicationMap>(*Consequent); 240 } else { 241 State = State->remove<NullImplicationMap>(Antecedent); 242 State = State->remove<NonNullImplicationMap>(*Consequent); 243 } 244 } 245 246 return State; 247 } 248}; 249 250} // end empty namespace 251
| 110 } 111 112 // Record an implication: index is non-null if the output is non-null. 113 if (interfaceHasSuperclass(ID, "NSDictionary") && 114 (Msg.getSelector() == ObjectForKeyedSubscriptSel || 115 Msg.getSelector() == ObjectForKeySel)) { 116 SymbolRef ArgS = Msg.getArgSVal(0).getAsSymbol(); 117 SymbolRef RetS = Msg.getReturnValue().getAsSymbol(); 118 119 if (ArgS && RetS) { 120 // Emulate an implication: the argument is non-null if 121 // the return value is non-null. 122 State = State->set<NonNullImplicationMap>(RetS, ArgS); 123 124 // Conversely, when the argument is null, the return value 125 // is definitely null. 126 State = State->set<NullImplicationMap>(ArgS, RetS); 127 } 128 } 129 130 C.addTransition(State); 131 } 132 133 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const { 134 ProgramStateRef State = C.getState(); 135 136 State = dropDeadFromGDM<NullImplicationMap>(SymReaper, State); 137 State = dropDeadFromGDM<NonNullImplicationMap>(SymReaper, State); 138 139 C.addTransition(State); 140 } 141 142private: 143 144 /// \returns State with GDM \p MapName where all dead symbols were 145 // removed. 146 template <typename MapName> 147 ProgramStateRef dropDeadFromGDM(SymbolReaper &SymReaper, 148 ProgramStateRef State) const { 149 for (const std::pair<SymbolRef, SymbolRef> &P : State->get<MapName>()) 150 if (!SymReaper.isLive(P.first) || !SymReaper.isLive(P.second)) 151 State = State->remove<MapName>(P.first); 152 return State; 153 } 154 155 /// \returns Whether we trust the result of the method call to be 156 /// a non-null pointer. 157 bool isNonNullPtr(const CallEvent &Call, CheckerContext &C) const { 158 QualType ExprRetType = Call.getResultType(); 159 if (!ExprRetType->isAnyPointerType()) 160 return false; 161 162 if (getNullabilityAnnotation(ExprRetType) == Nullability::Nonnull) 163 return true; 164 165 // The logic for ObjC instance method calls is more complicated, 166 // as the return value is nil when the receiver is nil. 167 if (!isa<ObjCMethodCall>(&Call)) 168 return false; 169 170 const auto *MCall = cast<ObjCMethodCall>(&Call); 171 const ObjCMethodDecl *MD = MCall->getDecl(); 172 173 // Distrust protocols. 174 if (isa<ObjCProtocolDecl>(MD->getDeclContext())) 175 return false; 176 177 QualType DeclRetType = MD->getReturnType(); 178 if (getNullabilityAnnotation(DeclRetType) != Nullability::Nonnull) 179 return false; 180 181 // For class messages it is sufficient for the declaration to be 182 // annotated _Nonnull. 183 if (!MCall->isInstanceMessage()) 184 return true; 185 186 // Alternatively, the analyzer could know that the receiver is not null. 187 SVal Receiver = MCall->getReceiverSVal(); 188 ConditionTruthVal TV = C.getState()->isNonNull(Receiver); 189 if (TV.isConstrainedTrue()) 190 return true; 191 192 return false; 193 } 194 195 /// \return Whether \p ID has a superclass by the name \p ClassName. 196 bool interfaceHasSuperclass(const ObjCInterfaceDecl *ID, 197 StringRef ClassName) const { 198 if (ID->getIdentifier()->getName() == ClassName) 199 return true; 200 201 if (const ObjCInterfaceDecl *Super = ID->getSuperClass()) 202 return interfaceHasSuperclass(Super, ClassName); 203 204 return false; 205 } 206 207 208 /// \return a state with an optional implication added (if exists) 209 /// from a map of recorded implications. 210 /// If \p Negated is true, checks NullImplicationMap, and assumes 211 /// the negation of \p Antecedent. 212 /// Checks NonNullImplicationMap and assumes \p Antecedent otherwise. 213 ProgramStateRef addImplication(SymbolRef Antecedent, 214 ProgramStateRef InputState, 215 bool Negated) const { 216 if (!InputState) 217 return nullptr; 218 SValBuilder &SVB = InputState->getStateManager().getSValBuilder(); 219 const SymbolRef *Consequent = 220 Negated ? InputState->get<NonNullImplicationMap>(Antecedent) 221 : InputState->get<NullImplicationMap>(Antecedent); 222 if (!Consequent) 223 return InputState; 224 225 SVal AntecedentV = SVB.makeSymbolVal(Antecedent); 226 ProgramStateRef State = InputState; 227 228 if ((Negated && InputState->isNonNull(AntecedentV).isConstrainedTrue()) 229 || (!Negated && InputState->isNull(AntecedentV).isConstrainedTrue())) { 230 SVal ConsequentS = SVB.makeSymbolVal(*Consequent); 231 State = InputState->assume(ConsequentS.castAs<DefinedSVal>(), Negated); 232 if (!State) 233 return nullptr; 234 235 // Drop implications from the map. 236 if (Negated) { 237 State = State->remove<NonNullImplicationMap>(Antecedent); 238 State = State->remove<NullImplicationMap>(*Consequent); 239 } else { 240 State = State->remove<NullImplicationMap>(Antecedent); 241 State = State->remove<NonNullImplicationMap>(*Consequent); 242 } 243 } 244 245 return State; 246 } 247}; 248 249} // end empty namespace 250
|
252
| |
253void ento::registerTrustNonnullChecker(CheckerManager &Mgr) { 254 Mgr.registerChecker<TrustNonnullChecker>(Mgr.getASTContext()); 255}
| 251void ento::registerTrustNonnullChecker(CheckerManager &Mgr) { 252 Mgr.registerChecker<TrustNonnullChecker>(Mgr.getASTContext()); 253}
|
| 254 255bool ento::shouldRegisterTrustNonnullChecker(const LangOptions &LO) { 256 return true; 257}
|
| |