brintos

brintos / llvm-project-archived public Read only

0
0
Text · 10.9 KiB · bc67391 Raw
318 lines · cpp
1//===--- NonNullParamChecker.cpp - Undefined arguments checker -*- 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 defines NonNullParamChecker, which checks for arguments expected not to10// be null due to:11//   - the corresponding parameters being declared to have nonnull attribute12//   - the corresponding parameters being references; since the call would form13//     a reference to a null pointer14//15//===----------------------------------------------------------------------===//16 17#include "clang/AST/Attr.h"18#include "clang/Analysis/AnyCall.h"19#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"20#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"21#include "clang/StaticAnalyzer/Core/Checker.h"22#include "clang/StaticAnalyzer/Core/CheckerManager.h"23#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"24#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"25#include "llvm/ADT/StringExtras.h"26 27using namespace clang;28using namespace ento;29 30namespace {31class NonNullParamChecker32    : public Checker<check::PreCall, check::BeginFunction,33                     EventDispatcher<ImplicitNullDerefEvent>> {34  const BugType BTAttrNonNull{35      this, "Argument with 'nonnull' attribute passed null", "API"};36  const BugType BTNullRefArg{this, "Dereference of null pointer"};37 38public:39  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;40  void checkBeginFunction(CheckerContext &C) const;41 42  std::unique_ptr<PathSensitiveBugReport>43  genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE,44                           unsigned IdxOfArg) const;45  std::unique_ptr<PathSensitiveBugReport>46  genReportReferenceToNullPointer(const ExplodedNode *ErrorN,47                                  const Expr *ArgE) const;48};49 50template <class CallType>51void setBitsAccordingToFunctionAttributes(const CallType &Call,52                                          llvm::SmallBitVector &AttrNonNull) {53  const Decl *FD = Call.getDecl();54 55  for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) {56    if (!NonNull->args_size()) {57      // Lack of attribute parameters means that all of the parameters are58      // implicitly marked as non-null.59      AttrNonNull.set();60      break;61    }62 63    for (const ParamIdx &Idx : NonNull->args()) {64      // 'nonnull' attribute's parameters are 1-based and should be adjusted to65      // match actual AST parameter/argument indices.66      unsigned IdxAST = Idx.getASTIndex();67      if (IdxAST >= AttrNonNull.size())68        continue;69      AttrNonNull.set(IdxAST);70    }71  }72}73 74template <class CallType>75void setBitsAccordingToParameterAttributes(const CallType &Call,76                                           llvm::SmallBitVector &AttrNonNull) {77  for (const ParmVarDecl *Parameter : Call.parameters()) {78    unsigned ParameterIndex = Parameter->getFunctionScopeIndex();79    if (ParameterIndex == AttrNonNull.size())80      break;81 82    if (Parameter->hasAttr<NonNullAttr>())83      AttrNonNull.set(ParameterIndex);84  }85}86 87template <class CallType>88llvm::SmallBitVector getNonNullAttrsImpl(const CallType &Call,89                                         unsigned ExpectedSize) {90  llvm::SmallBitVector AttrNonNull(ExpectedSize);91 92  setBitsAccordingToFunctionAttributes(Call, AttrNonNull);93  setBitsAccordingToParameterAttributes(Call, AttrNonNull);94 95  return AttrNonNull;96}97 98/// \return Bitvector marking non-null attributes.99llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) {100  return getNonNullAttrsImpl(Call, Call.getNumArgs());101}102 103/// \return Bitvector marking non-null attributes.104llvm::SmallBitVector getNonNullAttrs(const AnyCall &Call) {105  return getNonNullAttrsImpl(Call, Call.param_size());106}107} // end anonymous namespace108 109void NonNullParamChecker::checkPreCall(const CallEvent &Call,110                                       CheckerContext &C) const {111  if (!Call.getDecl())112    return;113 114  llvm::SmallBitVector AttrNonNull = getNonNullAttrs(Call);115  unsigned NumArgs = Call.getNumArgs();116 117  ProgramStateRef state = C.getState();118  ArrayRef<ParmVarDecl *> parms = Call.parameters();119 120  for (unsigned idx = 0; idx < NumArgs; ++idx) {121    // For vararg functions, a corresponding parameter decl may not exist.122    bool HasParam = idx < parms.size();123 124    // Check if the parameter is a reference. We want to report when reference125    // to a null pointer is passed as a parameter.126    bool HasRefTypeParam =127        HasParam ? parms[idx]->getType()->isReferenceType() : false;128    bool ExpectedToBeNonNull = AttrNonNull.test(idx);129 130    if (!ExpectedToBeNonNull && !HasRefTypeParam)131      continue;132 133    // If the value is unknown or undefined, we can't perform this check.134    const Expr *ArgE = Call.getArgExpr(idx);135    SVal V = Call.getArgSVal(idx);136    auto DV = V.getAs<DefinedSVal>();137    if (!DV)138      continue;139 140    assert(!HasRefTypeParam || isa<Loc>(*DV));141 142    // Process the case when the argument is not a location.143    if (ExpectedToBeNonNull && !isa<Loc>(*DV)) {144      // If the argument is a union type, we want to handle a potential145      // transparent_union GCC extension.146      if (!ArgE)147        continue;148 149      QualType T = ArgE->getType();150      const RecordType *UT = T->getAsUnionType();151      if (!UT ||152          !UT->getDecl()->getMostRecentDecl()->hasAttr<TransparentUnionAttr>())153        continue;154 155      auto CSV = DV->getAs<nonloc::CompoundVal>();156 157      // FIXME: Handle LazyCompoundVals?158      if (!CSV)159        continue;160 161      V = *(CSV->begin());162      DV = V.getAs<DefinedSVal>();163      assert(++CSV->begin() == CSV->end());164      // FIXME: Handle (some_union){ some_other_union_val }, which turns into165      // a LazyCompoundVal inside a CompoundVal.166      if (!isa<Loc>(V))167        continue;168 169      // Retrieve the corresponding expression.170      if (const auto *CE = dyn_cast<CompoundLiteralExpr>(ArgE))171        if (const auto *IE = dyn_cast<InitListExpr>(CE->getInitializer()))172          ArgE = dyn_cast<Expr>(*(IE->begin()));173    }174 175    ConstraintManager &CM = C.getConstraintManager();176    ProgramStateRef stateNotNull, stateNull;177    std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);178 179    // Generate an error node.  Check for a null node in case180    // we cache out.181    if (stateNull && !stateNotNull) {182      if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) {183 184        std::unique_ptr<BugReport> R;185        if (ExpectedToBeNonNull)186          R = genReportNullAttrNonNull(errorNode, ArgE, idx + 1);187        else if (HasRefTypeParam)188          R = genReportReferenceToNullPointer(errorNode, ArgE);189 190        // Highlight the range of the argument that was null.191        R->addRange(Call.getArgSourceRange(idx));192 193        // Emit the bug report.194        C.emitReport(std::move(R));195      }196 197      // Always return.  Either we cached out or we just emitted an error.198      return;199    }200 201    if (stateNull) {202      if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) {203        ImplicitNullDerefEvent event = {204            V, false, N, &C.getBugReporter(),205            /*IsDirectDereference=*/HasRefTypeParam};206        dispatchEvent(event);207      }208    }209 210    // If a pointer value passed the check we should assume that it is211    // indeed not null from this point forward.212    state = stateNotNull;213  }214 215  // If we reach here all of the arguments passed the nonnull check.216  // If 'state' has been updated generated a new node.217  C.addTransition(state);218}219 220/// We want to trust developer annotations and consider all 'nonnull' parameters221/// as non-null indeed. Each marked parameter will get a corresponding222/// constraint.223///224/// This approach will not only help us to get rid of some false positives, but225/// remove duplicates and shorten warning traces as well.226///227/// \code228///   void foo(int *x) [[gnu::nonnull]] {229///     // . . .230///     *x = 42;    // we don't want to consider this as an error...231///     // . . .232///   }233///234///   foo(nullptr); // ...and report here instead235/// \endcode236void NonNullParamChecker::checkBeginFunction(CheckerContext &Context) const {237  // Planned assumption makes sense only for top-level functions.238  // Inlined functions will get similar constraints as part of 'checkPreCall'.239  if (!Context.inTopFrame())240    return;241 242  const LocationContext *LocContext = Context.getLocationContext();243 244  const Decl *FD = LocContext->getDecl();245  // AnyCall helps us here to avoid checking for FunctionDecl and ObjCMethodDecl246  // separately and aggregates interfaces of these classes.247  auto AbstractCall = AnyCall::forDecl(FD);248  if (!AbstractCall)249    return;250 251  ProgramStateRef State = Context.getState();252  llvm::SmallBitVector ParameterNonNullMarks = getNonNullAttrs(*AbstractCall);253 254  for (const ParmVarDecl *Parameter : AbstractCall->parameters()) {255    // 1. Check parameter if it is annotated as non-null256    if (!ParameterNonNullMarks.test(Parameter->getFunctionScopeIndex()))257      continue;258 259    // 2. Check that parameter is a pointer.260    //    Nonnull attribute can be applied to non-pointers (by default261    //    __attribute__(nonnull) implies "all parameters").262    if (!Parameter->getType()->isPointerType())263      continue;264 265    Loc ParameterLoc = State->getLValue(Parameter, LocContext);266    // We never consider top-level function parameters undefined.267    auto StoredVal =268        State->getSVal(ParameterLoc).castAs<DefinedOrUnknownSVal>();269 270    // 3. Assume that it is indeed non-null271    if (ProgramStateRef NewState = State->assume(StoredVal, true)) {272      State = NewState;273    }274  }275 276  Context.addTransition(State);277}278 279std::unique_ptr<PathSensitiveBugReport>280NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode,281                                              const Expr *ArgE,282                                              unsigned IdxOfArg) const {283  llvm::SmallString<256> SBuf;284  llvm::raw_svector_ostream OS(SBuf);285  OS << "Null pointer passed to "286     << IdxOfArg << llvm::getOrdinalSuffix(IdxOfArg)287     << " parameter expecting 'nonnull'";288 289  auto R =290      std::make_unique<PathSensitiveBugReport>(BTAttrNonNull, SBuf, ErrorNode);291  if (ArgE)292    bugreporter::trackExpressionValue(ErrorNode, ArgE, *R);293 294  return R;295}296 297std::unique_ptr<PathSensitiveBugReport>298NonNullParamChecker::genReportReferenceToNullPointer(299    const ExplodedNode *ErrorNode, const Expr *ArgE) const {300  auto R = std::make_unique<PathSensitiveBugReport>(301      BTNullRefArg, "Forming reference to null pointer", ErrorNode);302  if (ArgE) {303    const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE);304    if (!ArgEDeref)305      ArgEDeref = ArgE;306    bugreporter::trackExpressionValue(ErrorNode, ArgEDeref, *R);307  }308  return R;309}310 311void ento::registerNonNullParamChecker(CheckerManager &mgr) {312  mgr.registerChecker<NonNullParamChecker>();313}314 315bool ento::shouldRegisterNonNullParamChecker(const CheckerManager &mgr) {316  return true;317}318