257 lines · cpp
1//== TrustNonnullChecker.cpp --------- API nullability modeling -*- 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-exception6//7//===----------------------------------------------------------------------===//8//9// This checker adds nullability-related assumptions:10//11// 1. Methods annotated with _Nonnull12// which come from system headers actually return a non-null pointer.13//14// 2. NSDictionary key is non-null after the keyword subscript operation15// 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 (SymbolRef Antecedent : CondS->symbols()) {73 State = addImplication(Antecedent, State, true);74 State = addImplication(Antecedent, State, false);75 }76 77 return State;78 }79 80 void checkPostCall(const CallEvent &Call, CheckerContext &C) const {81 // Only trust annotations for system headers for non-protocols.82 if (!Call.isInSystemHeader())83 return;84 85 ProgramStateRef State = C.getState();86 87 if (isNonNullPtr(Call, C))88 if (auto L = Call.getReturnValue().getAs<Loc>())89 State = State->assume(*L, /*assumption=*/true);90 91 C.addTransition(State);92 }93 94 void checkPostObjCMessage(const ObjCMethodCall &Msg,95 CheckerContext &C) const {96 const ObjCInterfaceDecl *ID = Msg.getReceiverInterface();97 if (!ID)98 return;99 100 ProgramStateRef State = C.getState();101 102 // Index to setter for NSMutableDictionary is assumed to be non-null,103 // as an exception is thrown otherwise.104 if (interfaceHasSuperclass(ID, "NSMutableDictionary") &&105 (Msg.getSelector() == SetObjectForKeyedSubscriptSel ||106 Msg.getSelector() == SetObjectForKeySel)) {107 if (auto L = Msg.getArgSVal(1).getAs<Loc>())108 State = State->assume(*L, /*assumption=*/true);109 }110 111 // Record an implication: index is non-null if the output is non-null.112 if (interfaceHasSuperclass(ID, "NSDictionary") &&113 (Msg.getSelector() == ObjectForKeyedSubscriptSel ||114 Msg.getSelector() == ObjectForKeySel)) {115 SymbolRef ArgS = Msg.getArgSVal(0).getAsSymbol();116 SymbolRef RetS = Msg.getReturnValue().getAsSymbol();117 118 if (ArgS && RetS) {119 // Emulate an implication: the argument is non-null if120 // the return value is non-null.121 State = State->set<NonNullImplicationMap>(RetS, ArgS);122 123 // Conversely, when the argument is null, the return value124 // is definitely null.125 State = State->set<NullImplicationMap>(ArgS, RetS);126 }127 }128 129 C.addTransition(State);130 }131 132 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const {133 ProgramStateRef State = C.getState();134 135 State = dropDeadFromGDM<NullImplicationMap>(SymReaper, State);136 State = dropDeadFromGDM<NonNullImplicationMap>(SymReaper, State);137 138 C.addTransition(State);139 }140 141private:142 143 /// \returns State with GDM \p MapName where all dead symbols were144 // removed.145 template <typename MapName>146 ProgramStateRef dropDeadFromGDM(SymbolReaper &SymReaper,147 ProgramStateRef State) const {148 for (const std::pair<SymbolRef, SymbolRef> &P : State->get<MapName>())149 if (!SymReaper.isLive(P.first) || !SymReaper.isLive(P.second))150 State = State->remove<MapName>(P.first);151 return State;152 }153 154 /// \returns Whether we trust the result of the method call to be155 /// a non-null pointer.156 bool isNonNullPtr(const CallEvent &Call, CheckerContext &C) const {157 QualType ExprRetType = Call.getResultType();158 if (!ExprRetType->isAnyPointerType())159 return false;160 161 if (getNullabilityAnnotation(ExprRetType) == Nullability::Nonnull)162 return true;163 164 // The logic for ObjC instance method calls is more complicated,165 // as the return value is nil when the receiver is nil.166 if (!isa<ObjCMethodCall>(&Call))167 return false;168 169 const auto *MCall = cast<ObjCMethodCall>(&Call);170 const ObjCMethodDecl *MD = MCall->getDecl();171 172 // Distrust protocols.173 if (isa<ObjCProtocolDecl>(MD->getDeclContext()))174 return false;175 176 QualType DeclRetType = MD->getReturnType();177 if (getNullabilityAnnotation(DeclRetType) != Nullability::Nonnull)178 return false;179 180 // For class messages it is sufficient for the declaration to be181 // annotated _Nonnull.182 if (!MCall->isInstanceMessage())183 return true;184 185 // Alternatively, the analyzer could know that the receiver is not null.186 SVal Receiver = MCall->getReceiverSVal();187 ConditionTruthVal TV = C.getState()->isNonNull(Receiver);188 if (TV.isConstrainedTrue())189 return true;190 191 return false;192 }193 194 /// \return Whether \p ID has a superclass by the name \p ClassName.195 bool interfaceHasSuperclass(const ObjCInterfaceDecl *ID,196 StringRef ClassName) const {197 if (ID->getIdentifier()->getName() == ClassName)198 return true;199 200 if (const ObjCInterfaceDecl *Super = ID->getSuperClass())201 return interfaceHasSuperclass(Super, ClassName);202 203 return false;204 }205 206 207 /// \return a state with an optional implication added (if exists)208 /// from a map of recorded implications.209 /// If \p Negated is true, checks NullImplicationMap, and assumes210 /// the negation of \p Antecedent.211 /// Checks NonNullImplicationMap and assumes \p Antecedent otherwise.212 ProgramStateRef addImplication(SymbolRef Antecedent,213 ProgramStateRef InputState,214 bool Negated) const {215 if (!InputState)216 return nullptr;217 SValBuilder &SVB = InputState->getStateManager().getSValBuilder();218 const SymbolRef *Consequent =219 Negated ? InputState->get<NonNullImplicationMap>(Antecedent)220 : InputState->get<NullImplicationMap>(Antecedent);221 if (!Consequent)222 return InputState;223 224 SVal AntecedentV = SVB.makeSymbolVal(Antecedent);225 ProgramStateRef State = InputState;226 227 if ((Negated && InputState->isNonNull(AntecedentV).isConstrainedTrue())228 || (!Negated && InputState->isNull(AntecedentV).isConstrainedTrue())) {229 SVal ConsequentS = SVB.makeSymbolVal(*Consequent);230 State = InputState->assume(ConsequentS.castAs<DefinedSVal>(), Negated);231 if (!State)232 return nullptr;233 234 // Drop implications from the map.235 if (Negated) {236 State = State->remove<NonNullImplicationMap>(Antecedent);237 State = State->remove<NullImplicationMap>(*Consequent);238 } else {239 State = State->remove<NullImplicationMap>(Antecedent);240 State = State->remove<NonNullImplicationMap>(*Consequent);241 }242 }243 244 return State;245 }246};247 248} // end empty namespace249 250void ento::registerTrustNonnullChecker(CheckerManager &Mgr) {251 Mgr.registerChecker<TrustNonnullChecker>(Mgr.getASTContext());252}253 254bool ento::shouldRegisterTrustNonnullChecker(const CheckerManager &mgr) {255 return true;256}257