brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.5 KiB · 4937af3 Raw
187 lines · cpp
1//== ObjCContainersChecker.cpp - Path sensitive checker for CFArray *- 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// Performs path sensitive checks of Core Foundation static containers like10// CFArray.11// 1) Check for buffer overflows:12//      In CFArrayGetArrayAtIndex( myArray, index), if the index is outside the13//      index space of theArray (0 to N-1 inclusive (where N is the count of14//      theArray), the behavior is undefined.15//16//===----------------------------------------------------------------------===//17 18#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"19#include "clang/AST/ParentMap.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/CheckerContext.h"24#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"25 26using namespace clang;27using namespace ento;28 29namespace {30class ObjCContainersChecker : public Checker< check::PreStmt<CallExpr>,31                                             check::PostStmt<CallExpr>,32                                             check::PointerEscape> {33  const BugType BT{this, "CFArray API", categories::CoreFoundationObjectiveC};34 35  inline SymbolRef getArraySym(const Expr *E, CheckerContext &C) const {36    SVal ArrayRef = C.getSVal(E);37    SymbolRef ArraySym = ArrayRef.getAsSymbol();38    return ArraySym;39  }40 41  void addSizeInfo(const Expr *Array, const Expr *Size,42                   CheckerContext &C) const;43 44public:45  void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;46  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;47  ProgramStateRef checkPointerEscape(ProgramStateRef State,48                                     const InvalidatedSymbols &Escaped,49                                     const CallEvent *Call,50                                     PointerEscapeKind Kind) const;51 52  void printState(raw_ostream &OS, ProgramStateRef State,53                  const char *NL, const char *Sep) const override;54};55} // end anonymous namespace56 57// ProgramState trait - a map from array symbol to its state.58REGISTER_MAP_WITH_PROGRAMSTATE(ArraySizeMap, SymbolRef, DefinedSVal)59 60void ObjCContainersChecker::addSizeInfo(const Expr *Array, const Expr *Size,61                                        CheckerContext &C) const {62  ProgramStateRef State = C.getState();63  SVal SizeV = C.getSVal(Size);64  // Undefined is reported by another checker.65  if (SizeV.isUnknownOrUndef())66    return;67 68  // Get the ArrayRef symbol.69  SVal ArrayRef = C.getSVal(Array);70  SymbolRef ArraySym = ArrayRef.getAsSymbol();71  if (!ArraySym)72    return;73 74  C.addTransition(75      State->set<ArraySizeMap>(ArraySym, SizeV.castAs<DefinedSVal>()));76}77 78void ObjCContainersChecker::checkPostStmt(const CallExpr *CE,79                                          CheckerContext &C) const {80  StringRef Name = C.getCalleeName(CE);81  if (Name.empty() || CE->getNumArgs() < 1)82    return;83 84  // Add array size information to the state.85  if (Name == "CFArrayCreate") {86    if (CE->getNumArgs() < 3)87      return;88    // Note, we can visit the Create method in the post-visit because89    // the CFIndex parameter is passed in by value and will not be invalidated90    // by the call.91    addSizeInfo(CE, CE->getArg(2), C);92    return;93  }94 95  if (Name == "CFArrayGetCount") {96    addSizeInfo(CE->getArg(0), CE, C);97    return;98  }99}100 101void ObjCContainersChecker::checkPreStmt(const CallExpr *CE,102                                         CheckerContext &C) const {103  StringRef Name = C.getCalleeName(CE);104  if (Name.empty() || CE->getNumArgs() < 2)105    return;106 107  // Check the array access.108  if (Name == "CFArrayGetValueAtIndex") {109    ProgramStateRef State = C.getState();110    // Retrieve the size.111    // Find out if we saw this array symbol before and have information about112    // it.113    const Expr *ArrayExpr = CE->getArg(0);114    SymbolRef ArraySym = getArraySym(ArrayExpr, C);115    if (!ArraySym)116      return;117 118    const DefinedSVal *Size = State->get<ArraySizeMap>(ArraySym);119 120    if (!Size)121      return;122 123    // Get the index.124    const Expr *IdxExpr = CE->getArg(1);125    SVal IdxVal = C.getSVal(IdxExpr);126    if (IdxVal.isUnknownOrUndef())127      return;128    DefinedSVal Idx = IdxVal.castAs<DefinedSVal>();129 130    // Now, check if 'Idx in [0, Size-1]'.131    const QualType T = IdxExpr->getType();132    ProgramStateRef StInBound, StOutBound;133    std::tie(StInBound, StOutBound) = State->assumeInBoundDual(Idx, *Size, T);134    if (StOutBound && !StInBound) {135      ExplodedNode *N = C.generateErrorNode(StOutBound);136      if (!N)137        return;138 139      auto R = std::make_unique<PathSensitiveBugReport>(140          BT, "Index is out of bounds", N);141      R->addRange(IdxExpr->getSourceRange());142      bugreporter::trackExpressionValue(N, IdxExpr, *R,143                                        {bugreporter::TrackingKind::Thorough,144                                         /*EnableNullFPSuppression=*/false});145      C.emitReport(std::move(R));146      return;147    }148  }149}150 151ProgramStateRef152ObjCContainersChecker::checkPointerEscape(ProgramStateRef State,153                                          const InvalidatedSymbols &Escaped,154                                          const CallEvent *Call,155                                          PointerEscapeKind Kind) const {156  for (const auto &Sym : Escaped) {157    // When a symbol for a mutable array escapes, we can't reason precisely158    // about its size any more -- so remove it from the map.159    // Note that we aren't notified here when a CFMutableArrayRef escapes as a160    // CFArrayRef. This is because CFArrayRef is typedef'd as a pointer to a161    // const-qualified type.162    State = State->remove<ArraySizeMap>(Sym);163  }164  return State;165}166 167void ObjCContainersChecker::printState(raw_ostream &OS, ProgramStateRef State,168                                       const char *NL, const char *Sep) const {169  ArraySizeMapTy Map = State->get<ArraySizeMap>();170  if (Map.isEmpty())171    return;172 173  OS << Sep << "ObjC container sizes :" << NL;174  for (auto I : Map) {175    OS << I.first << " : " << I.second << NL;176  }177}178 179/// Register checker.180void ento::registerObjCContainersChecker(CheckerManager &mgr) {181  mgr.registerChecker<ObjCContainersChecker>();182}183 184bool ento::shouldRegisterObjCContainersChecker(const CheckerManager &mgr) {185  return true;186}187