brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.1 KiB · b5e3249 Raw
154 lines · cpp
1//==- NonnullGlobalConstantsChecker.cpp ---------------------------*- 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 an assumption that constant globals of certain types* are10//  non-null, as otherwise they generally do not convey any useful information.11//  The assumption is useful, as many framework use e. g. global const strings,12//  and the analyzer might not be able to infer the global value if the13//  definition is in a separate translation unit.14//  The following types (and their typedef aliases) are considered to be15//  non-null:16//   - `char* const`17//   - `const CFStringRef` from CoreFoundation18//   - `NSString* const` from Foundation19//   - `CFBooleanRef` from Foundation20//21//===----------------------------------------------------------------------===//22 23#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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/ExprEngine.h"28#include <optional>29 30using namespace clang;31using namespace ento;32 33namespace {34 35class NonnullGlobalConstantsChecker : public Checker<check::Location> {36  mutable IdentifierInfo *NSStringII = nullptr;37  mutable IdentifierInfo *CFStringRefII = nullptr;38  mutable IdentifierInfo *CFBooleanRefII = nullptr;39  mutable IdentifierInfo *CFNullRefII = nullptr;40 41public:42  NonnullGlobalConstantsChecker() {}43 44  void checkLocation(SVal l, bool isLoad, const Stmt *S,45                     CheckerContext &C) const;46 47private:48  void initIdentifierInfo(ASTContext &Ctx) const;49 50  bool isGlobalConstString(SVal V) const;51 52  bool isNonnullType(QualType Ty) const;53};54 55} // namespace56 57/// Lazily initialize cache for required identifier information.58void NonnullGlobalConstantsChecker::initIdentifierInfo(ASTContext &Ctx) const {59  if (NSStringII)60    return;61 62  NSStringII = &Ctx.Idents.get("NSString");63  CFStringRefII = &Ctx.Idents.get("CFStringRef");64  CFBooleanRefII = &Ctx.Idents.get("CFBooleanRef");65  CFNullRefII = &Ctx.Idents.get("CFNullRef");66}67 68/// Add an assumption that const string-like globals are non-null.69void NonnullGlobalConstantsChecker::checkLocation(SVal location, bool isLoad,70                                                 const Stmt *S,71                                                 CheckerContext &C) const {72  initIdentifierInfo(C.getASTContext());73  if (!isLoad || !location.isValid())74    return;75 76  ProgramStateRef State = C.getState();77 78  if (isGlobalConstString(location)) {79    SVal V = State->getSVal(location.castAs<Loc>());80    std::optional<DefinedOrUnknownSVal> Constr =81        V.getAs<DefinedOrUnknownSVal>();82 83    if (Constr) {84 85      // Assume that the variable is non-null.86      ProgramStateRef OutputState = State->assume(*Constr, true);87      C.addTransition(OutputState);88    }89  }90}91 92/// \param V loaded lvalue.93/// \return whether @c val is a string-like const global.94bool NonnullGlobalConstantsChecker::isGlobalConstString(SVal V) const {95  std::optional<loc::MemRegionVal> RegionVal = V.getAs<loc::MemRegionVal>();96  if (!RegionVal)97    return false;98  auto *Region = dyn_cast<VarRegion>(RegionVal->getAsRegion());99  if (!Region)100    return false;101  const VarDecl *Decl = Region->getDecl();102 103  if (!Decl->hasGlobalStorage())104    return false;105 106  QualType Ty = Decl->getType();107  bool HasConst = Ty.isConstQualified();108  if (isNonnullType(Ty) && HasConst)109    return true;110 111  // Look through the typedefs.112  while (const Type *T = Ty.getTypePtr()) {113    if (const auto *AT = dyn_cast<AttributedType>(T)) {114      if (AT->getAttrKind() == attr::TypeNonNull)115        return true;116      Ty = AT->getModifiedType();117    } else if (const auto *TT = dyn_cast<TypedefType>(T)) {118      Ty = TT->getDecl()->getUnderlyingType();119      // It is sufficient for any intermediate typedef120      // to be classified const.121      HasConst = HasConst || Ty.isConstQualified();122      if (isNonnullType(Ty) && HasConst)123        return true;124    } else {125      return false;126    }127  }128  return false;129}130 131/// \return whether @c type is extremely unlikely to be null132bool NonnullGlobalConstantsChecker::isNonnullType(QualType Ty) const {133 134  if (Ty->isPointerType() && Ty->getPointeeType()->isCharType())135    return true;136 137  if (auto *T = dyn_cast<ObjCObjectPointerType>(Ty)) {138    return T->getInterfaceDecl() &&139      T->getInterfaceDecl()->getIdentifier() == NSStringII;140  } else if (auto *T = Ty->getAs<TypedefType>()) {141    IdentifierInfo* II = T->getDecl()->getIdentifier();142    return II == CFStringRefII || II == CFBooleanRefII || II == CFNullRefII;143  }144  return false;145}146 147void ento::registerNonnullGlobalConstantsChecker(CheckerManager &Mgr) {148  Mgr.registerChecker<NonnullGlobalConstantsChecker>();149}150 151bool ento::shouldRegisterNonnullGlobalConstantsChecker(const CheckerManager &mgr) {152  return true;153}154