forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			193 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			193 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			C++
		
	
	
	
//=== VLASizeChecker.cpp - Undefined dereference checker --------*- C++ -*-===//
 | 
						|
//
 | 
						|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | 
						|
// See https://llvm.org/LICENSE.txt for license information.
 | 
						|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
//
 | 
						|
// This defines VLASizeChecker, a builtin check in ExprEngine that
 | 
						|
// performs checks for declaration of VLA of undefined or zero size.
 | 
						|
// In addition, VLASizeChecker is responsible for defining the extent
 | 
						|
// of the MemRegion that represents a VLA.
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#include "Taint.h"
 | 
						|
#include "clang/AST/CharUnits.h"
 | 
						|
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 | 
						|
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 | 
						|
#include "clang/StaticAnalyzer/Core/Checker.h"
 | 
						|
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
 | 
						|
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
 | 
						|
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
 | 
						|
#include "llvm/ADT/STLExtras.h"
 | 
						|
#include "llvm/ADT/SmallString.h"
 | 
						|
#include "llvm/Support/raw_ostream.h"
 | 
						|
 | 
						|
using namespace clang;
 | 
						|
using namespace ento;
 | 
						|
using namespace taint;
 | 
						|
 | 
						|
namespace {
 | 
						|
class VLASizeChecker : public Checker< check::PreStmt<DeclStmt> > {
 | 
						|
  mutable std::unique_ptr<BugType> BT;
 | 
						|
  enum VLASize_Kind { VLA_Garbage, VLA_Zero, VLA_Tainted, VLA_Negative };
 | 
						|
 | 
						|
  void reportBug(VLASize_Kind Kind, const Expr *SizeE, ProgramStateRef State,
 | 
						|
                 CheckerContext &C,
 | 
						|
                 std::unique_ptr<BugReporterVisitor> Visitor = nullptr) const;
 | 
						|
 | 
						|
public:
 | 
						|
  void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const;
 | 
						|
};
 | 
						|
} // end anonymous namespace
 | 
						|
 | 
						|
void VLASizeChecker::reportBug(
 | 
						|
    VLASize_Kind Kind, const Expr *SizeE, ProgramStateRef State,
 | 
						|
    CheckerContext &C, std::unique_ptr<BugReporterVisitor> Visitor) const {
 | 
						|
  // Generate an error node.
 | 
						|
  ExplodedNode *N = C.generateErrorNode(State);
 | 
						|
  if (!N)
 | 
						|
    return;
 | 
						|
 | 
						|
  if (!BT)
 | 
						|
    BT.reset(new BuiltinBug(
 | 
						|
        this, "Dangerous variable-length array (VLA) declaration"));
 | 
						|
 | 
						|
  SmallString<256> buf;
 | 
						|
  llvm::raw_svector_ostream os(buf);
 | 
						|
  os << "Declared variable-length array (VLA) ";
 | 
						|
  switch (Kind) {
 | 
						|
  case VLA_Garbage:
 | 
						|
    os << "uses a garbage value as its size";
 | 
						|
    break;
 | 
						|
  case VLA_Zero:
 | 
						|
    os << "has zero size";
 | 
						|
    break;
 | 
						|
  case VLA_Tainted:
 | 
						|
    os << "has tainted size";
 | 
						|
    break;
 | 
						|
  case VLA_Negative:
 | 
						|
    os << "has negative size";
 | 
						|
    break;
 | 
						|
  }
 | 
						|
 | 
						|
  auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
 | 
						|
  report->addVisitor(std::move(Visitor));
 | 
						|
  report->addRange(SizeE->getSourceRange());
 | 
						|
  bugreporter::trackExpressionValue(N, SizeE, *report);
 | 
						|
  C.emitReport(std::move(report));
 | 
						|
}
 | 
						|
 | 
						|
void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
 | 
						|
  if (!DS->isSingleDecl())
 | 
						|
    return;
 | 
						|
 | 
						|
  const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
 | 
						|
  if (!VD)
 | 
						|
    return;
 | 
						|
 | 
						|
  ASTContext &Ctx = C.getASTContext();
 | 
						|
  const VariableArrayType *VLA = Ctx.getAsVariableArrayType(VD->getType());
 | 
						|
  if (!VLA)
 | 
						|
    return;
 | 
						|
 | 
						|
  // FIXME: Handle multi-dimensional VLAs.
 | 
						|
  const Expr *SE = VLA->getSizeExpr();
 | 
						|
  ProgramStateRef state = C.getState();
 | 
						|
  SVal sizeV = C.getSVal(SE);
 | 
						|
 | 
						|
  if (sizeV.isUndef()) {
 | 
						|
    reportBug(VLA_Garbage, SE, state, C);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // See if the size value is known. It can't be undefined because we would have
 | 
						|
  // warned about that already.
 | 
						|
  if (sizeV.isUnknown())
 | 
						|
    return;
 | 
						|
 | 
						|
  // Check if the size is tainted.
 | 
						|
  if (isTainted(state, sizeV)) {
 | 
						|
    reportBug(VLA_Tainted, SE, nullptr, C,
 | 
						|
              std::make_unique<TaintBugVisitor>(sizeV));
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // Check if the size is zero.
 | 
						|
  DefinedSVal sizeD = sizeV.castAs<DefinedSVal>();
 | 
						|
 | 
						|
  ProgramStateRef stateNotZero, stateZero;
 | 
						|
  std::tie(stateNotZero, stateZero) = state->assume(sizeD);
 | 
						|
 | 
						|
  if (stateZero && !stateNotZero) {
 | 
						|
    reportBug(VLA_Zero, SE, stateZero, C);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // From this point on, assume that the size is not zero.
 | 
						|
  state = stateNotZero;
 | 
						|
 | 
						|
  // VLASizeChecker is responsible for defining the extent of the array being
 | 
						|
  // declared. We do this by multiplying the array length by the element size,
 | 
						|
  // then matching that with the array region's extent symbol.
 | 
						|
 | 
						|
  // Check if the size is negative.
 | 
						|
  SValBuilder &svalBuilder = C.getSValBuilder();
 | 
						|
 | 
						|
  QualType Ty = SE->getType();
 | 
						|
  DefinedOrUnknownSVal Zero = svalBuilder.makeZeroVal(Ty);
 | 
						|
 | 
						|
  SVal LessThanZeroVal = svalBuilder.evalBinOp(state, BO_LT, sizeD, Zero, Ty);
 | 
						|
  if (Optional<DefinedSVal> LessThanZeroDVal =
 | 
						|
        LessThanZeroVal.getAs<DefinedSVal>()) {
 | 
						|
    ConstraintManager &CM = C.getConstraintManager();
 | 
						|
    ProgramStateRef StatePos, StateNeg;
 | 
						|
 | 
						|
    std::tie(StateNeg, StatePos) = CM.assumeDual(state, *LessThanZeroDVal);
 | 
						|
    if (StateNeg && !StatePos) {
 | 
						|
      reportBug(VLA_Negative, SE, state, C);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    state = StatePos;
 | 
						|
  }
 | 
						|
 | 
						|
  // Convert the array length to size_t.
 | 
						|
  QualType SizeTy = Ctx.getSizeType();
 | 
						|
  NonLoc ArrayLength =
 | 
						|
      svalBuilder.evalCast(sizeD, SizeTy, SE->getType()).castAs<NonLoc>();
 | 
						|
 | 
						|
  // Get the element size.
 | 
						|
  CharUnits EleSize = Ctx.getTypeSizeInChars(VLA->getElementType());
 | 
						|
  SVal EleSizeVal = svalBuilder.makeIntVal(EleSize.getQuantity(), SizeTy);
 | 
						|
 | 
						|
  // Multiply the array length by the element size.
 | 
						|
  SVal ArraySizeVal = svalBuilder.evalBinOpNN(
 | 
						|
      state, BO_Mul, ArrayLength, EleSizeVal.castAs<NonLoc>(), SizeTy);
 | 
						|
 | 
						|
  // Finally, assume that the array's size matches the given size.
 | 
						|
  const LocationContext *LC = C.getLocationContext();
 | 
						|
  DefinedOrUnknownSVal DynSize =
 | 
						|
      getDynamicSize(state, state->getRegion(VD, LC), svalBuilder);
 | 
						|
 | 
						|
  DefinedOrUnknownSVal ArraySize = ArraySizeVal.castAs<DefinedOrUnknownSVal>();
 | 
						|
  DefinedOrUnknownSVal sizeIsKnown =
 | 
						|
      svalBuilder.evalEQ(state, DynSize, ArraySize);
 | 
						|
  state = state->assume(sizeIsKnown, true);
 | 
						|
 | 
						|
  // Assume should not fail at this point.
 | 
						|
  assert(state);
 | 
						|
 | 
						|
  // Remember our assumptions!
 | 
						|
  C.addTransition(state);
 | 
						|
}
 | 
						|
 | 
						|
void ento::registerVLASizeChecker(CheckerManager &mgr) {
 | 
						|
  mgr.registerChecker<VLASizeChecker>();
 | 
						|
}
 | 
						|
 | 
						|
bool ento::shouldRegisterVLASizeChecker(const LangOptions &LO) {
 | 
						|
  return true;
 | 
						|
}
 |