Example #1
0
void UnixAPIChecker::checkPreStmt(const CallExpr *CE,
                                  CheckerContext &C) const {
  const FunctionDecl *FD = C.getCalleeDecl(CE);
  if (!FD || FD->getKind() != Decl::Function)
    return;

  // Don't treat functions in namespaces with the same name a Unix function
  // as a call to the Unix function.
  const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext();
  if (NamespaceCtx && isa<NamespaceDecl>(NamespaceCtx))
    return;

  StringRef FName = C.getCalleeName(FD);
  if (FName.empty())
    return;

  SubChecker SC =
    llvm::StringSwitch<SubChecker>(FName)
      .Case("open", &UnixAPIChecker::CheckOpen)
      .Case("openat", &UnixAPIChecker::CheckOpenAt)
      .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce)
      .Case("calloc", &UnixAPIChecker::CheckCallocZero)
      .Case("malloc", &UnixAPIChecker::CheckMallocZero)
      .Case("realloc", &UnixAPIChecker::CheckReallocZero)
      .Case("reallocf", &UnixAPIChecker::CheckReallocfZero)
      .Cases("alloca", "__builtin_alloca", &UnixAPIChecker::CheckAllocaZero)
      .Case("__builtin_alloca_with_align",
            &UnixAPIChecker::CheckAllocaWithAlignZero)
      .Case("valloc", &UnixAPIChecker::CheckVallocZero)
      .Default(nullptr);

  if (SC)
    (this->*SC)(C, CE);
}
void GenericTaintChecker::addSourcesPre(const CallExpr *CE,
                                        CheckerContext &C) const {
  ProgramStateRef State = nullptr;
  const FunctionDecl *FDecl = C.getCalleeDecl(CE);
  if (!FDecl || FDecl->getKind() != Decl::Function)
    return;

  StringRef Name = C.getCalleeName(FDecl);
  if (Name.empty())
    return;

  // First, try generating a propagation rule for this function.
  TaintPropagationRule Rule =
    TaintPropagationRule::getTaintPropagationRule(FDecl, Name, C);
  if (!Rule.isNull()) {
    State = Rule.process(CE, C);
    if (!State)
      return;
    C.addTransition(State);
    return;
  }

  // Otherwise, check if we have custom pre-processing implemented.
  FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
    .Case("fscanf", &GenericTaintChecker::preFscanf)
    .Default(nullptr);
  // Check and evaluate the call.
  if (evalFunction)
    State = (this->*evalFunction)(CE, C);
  if (!State)
    return;
  C.addTransition(State);

}
static bool getPrintfFormatArgumentNum(const CallExpr *CE,
                                       const CheckerContext &C,
                                       unsigned int &ArgNum) {
  // Find if the function contains a format string argument.
  // Handles: fprintf, printf, sprintf, snprintf, vfprintf, vprintf, vsprintf,
  // vsnprintf, syslog, custom annotated functions.
  const FunctionDecl *FDecl = C.getCalleeDecl(CE);
  if (!FDecl)
    return false;
  for (specific_attr_iterator<FormatAttr>
         i = FDecl->specific_attr_begin<FormatAttr>(),
         e = FDecl->specific_attr_end<FormatAttr>(); i != e ; ++i) {

    const FormatAttr *Format = *i;
    ArgNum = Format->getFormatIdx() - 1;
    if ((Format->getType() == "printf") && CE->getNumArgs() > ArgNum)
      return true;
  }

  // Or if a function is named setproctitle (this is a heuristic).
  if (C.getCalleeName(CE).find("setproctitle") != StringRef::npos) {
    ArgNum = 0;
    return true;
  }

  return false;
}
void GenericTaintChecker::addSourcesPost(const CallExpr *CE,
                                         CheckerContext &C) const {
  // Define the attack surface.
  // Set the evaluation function by switching on the callee name.
  const FunctionDecl *FDecl = C.getCalleeDecl(CE);
  if (!FDecl || FDecl->getKind() != Decl::Function)
    return;

  StringRef Name = C.getCalleeName(FDecl);
  if (Name.empty())
    return;
  FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
    .Case("scanf", &GenericTaintChecker::postScanf)
    // TODO: Add support for vfscanf & family.
    .Case("getchar", &GenericTaintChecker::postRetTaint)
    .Case("getchar_unlocked", &GenericTaintChecker::postRetTaint)
    .Case("getenv", &GenericTaintChecker::postRetTaint)
    .Case("fopen", &GenericTaintChecker::postRetTaint)
    .Case("fdopen", &GenericTaintChecker::postRetTaint)
    .Case("freopen", &GenericTaintChecker::postRetTaint)
    .Case("getch", &GenericTaintChecker::postRetTaint)
    .Case("wgetch", &GenericTaintChecker::postRetTaint)
    .Case("socket", &GenericTaintChecker::postSocket)
    .Default(nullptr);

  // If the callee isn't defined, it is not of security concern.
  // Check and evaluate the call.
  ProgramStateRef State = nullptr;
  if (evalFunction)
    State = (this->*evalFunction)(CE, C);
  if (!State)
    return;

  C.addTransition(State);
}
Example #5
0
void UnixAPIChecker::checkPreStmt(const CallExpr *CE,
                                  CheckerContext &C) const {
  const FunctionDecl *FD = C.getCalleeDecl(CE);
  if (!FD || FD->getKind() != Decl::Function)
    return;

  StringRef FName = C.getCalleeName(FD);
  if (FName.empty())
    return;

  SubChecker SC =
    llvm::StringSwitch<SubChecker>(FName)
      .Case("open", &UnixAPIChecker::CheckOpen)
      .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce)
      .Case("calloc", &UnixAPIChecker::CheckCallocZero)
      .Case("malloc", &UnixAPIChecker::CheckMallocZero)
      .Case("realloc", &UnixAPIChecker::CheckReallocZero)
      .Case("reallocf", &UnixAPIChecker::CheckReallocfZero)
      .Cases("alloca", "__builtin_alloca", &UnixAPIChecker::CheckAllocaZero)
      .Case("valloc", &UnixAPIChecker::CheckVallocZero)
      .Default(nullptr);

  if (SC)
    (this->*SC)(C, CE);
}
Example #6
0
void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
                                       CheckerContext &C) const {
  ProgramStateRef state = C.getState();
  const LocationContext *LCtx = C.getLocationContext();
  if (state->getSVal(B, LCtx).isUndef()) {

    // Do not report assignments of uninitialized values inside swap functions.
    // This should allow to swap partially uninitialized structs
    // (radar://14129997)
    if (const FunctionDecl *EnclosingFunctionDecl =
        dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()))
      if (C.getCalleeName(EnclosingFunctionDecl) == "swap")
        return;

    // Generate an error node.
    ExplodedNode *N = C.generateErrorNode();
    if (!N)
      return;

    if (!BT)
      BT.reset(
          new BuiltinBug(this, "Result of operation is garbage or undefined"));

    SmallString<256> sbuf;
    llvm::raw_svector_ostream OS(sbuf);
    const Expr *Ex = nullptr;
    bool isLeft = true;

    if (state->getSVal(B->getLHS(), LCtx).isUndef()) {
      Ex = B->getLHS()->IgnoreParenCasts();
      isLeft = true;
    }
    else if (state->getSVal(B->getRHS(), LCtx).isUndef()) {
      Ex = B->getRHS()->IgnoreParenCasts();
      isLeft = false;
    }

    if (Ex) {
      OS << "The " << (isLeft ? "left" : "right")
         << " operand of '"
         << BinaryOperator::getOpcodeStr(B->getOpcode())
         << "' is a garbage value";
    }
    else {
      // Neither operand was undefined, but the result is undefined.
      OS << "The result of the '"
         << BinaryOperator::getOpcodeStr(B->getOpcode())
         << "' expression is undefined";
    }
    auto report = llvm::make_unique<BugReport>(*BT, OS.str(), N);
    if (Ex) {
      report->addRange(Ex->getSourceRange());
      bugreporter::trackNullOrUndefValue(N, Ex, *report);
    }
    else
      bugreporter::trackNullOrUndefValue(N, B, *report);

    C.emitReport(std::move(report));
  }
}
void DoubleFetchChecker::checkPostStmt(const CallExpr *CE, CheckerContext &Ctx) const{
	ProgramStateRef state = Ctx.getState();
	const FunctionDecl *FDecl = Ctx.getCalleeDecl(CE);
	StringRef funcName = Ctx.getCalleeName(FDecl);
	//std::cout<<"[checkPostStmt<CallExpr>] func name is:"<<funcName<<std::endl;
	//printf("[checkPostStmt<CallExpr>] func name is:%s\n",funcName);

}
void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
        const Stmt *StoreE,
        CheckerContext &C) const {
    if (!val.isUndef())
        return;

    // Do not report assignments of uninitialized values inside swap functions.
    // This should allow to swap partially uninitialized structs
    // (radar://14129997)
    if (const FunctionDecl *EnclosingFunctionDecl =
                dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()))
        if (C.getCalleeName(EnclosingFunctionDecl) == "swap")
            return;

    ExplodedNode *N = C.generateSink();

    if (!N)
        return;

    const char *str = "Assigned value is garbage or undefined";

    if (!BT)
        BT.reset(new BuiltinBug(str));

    // Generate a report for this bug.
    const Expr *ex = 0;

    while (StoreE) {
        if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) {
            if (B->isCompoundAssignmentOp()) {
                ProgramStateRef state = C.getState();
                if (state->getSVal(B->getLHS(), C.getLocationContext()).isUndef()) {
                    str = "The left expression of the compound assignment is an "
                          "uninitialized value. The computed value will also be garbage";
                    ex = B->getLHS();
                    break;
                }
            }

            ex = B->getRHS();
            break;
        }

        if (const DeclStmt *DS = dyn_cast<DeclStmt>(StoreE)) {
            const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
            ex = VD->getInit();
        }

        break;
    }

    BugReport *R = new BugReport(*BT, str, N);
    if (ex) {
        R->addRange(ex->getSourceRange());
        bugreporter::trackNullOrUndefValue(N, ex, *R);
    }
    C.emitReport(R);
}
Example #9
0
bool getParamDumper::evalCall(const CallExpr *CE, CheckerContext &C) const {

  FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))
    .Case("getParameter",&getParamDumper::analyzerEval)
    .Case("getUntrackedParameter",&getParamDumper::analyzerEval)
    .Default(nullptr);

  if (!Handler) return false;

  (this->*Handler)(CE,C);

  return true;
}
Example #10
0
void DoubleFetchChecker::checkPreStmt(const CallExpr *CE, CheckerContext &Ctx) const{
	ProgramStateRef state = Ctx.getState();
	const FunctionDecl *FDecl = Ctx.getCalleeDecl(CE);
	StringRef funcName = Ctx.getCalleeName(FDecl);
	//std::cout<<"[checkPreStmt<CallExpr>] func name is:"<<funcName.<<std::endl;
	//printf("[checkPreStmt<CallExpr>] func name is:%s\n",funcName);
	//std::cout<<"-------------------->getLocStart: "<<CE->getLocStart().getRawEncoding()<<std::endl;
	//std::cout<<"--------------------->getLocEnd: "<<CE->getLocEnd().getRawEncoding()<<std::endl;
	//std::cout<<"-------------------->getExprLoc: "<<CE->getExprLoc().getRawEncoding()<<std::endl;

	unsigned int spelling = Ctx.getSourceManager().getSpellingLineNumber(CE->getExprLoc());
	unsigned int ex = Ctx.getSourceManager().getExpansionLineNumber(CE->getExprLoc());

}
Example #11
0
void DoubleFetchChecker::checkPostStmt(const CallExpr *CE, CheckerContext &Ctx) const{
	ProgramStateRef state = Ctx.getState();
	const FunctionDecl *FDecl = Ctx.getCalleeDecl(CE);
	StringRef funcName = Ctx.getCalleeName(FDecl);

	/*
	  	  	const Expr * arg0 = CE->getArg(0);
			std::cout<<"[checkPostStmt]--->arg0: "<<toStr(arg0)<<std::endl;
			SourceLocation Loc0 = arg0->getExprLoc();
			StringRef name0 = Ctx.getMacroNameOrSpelling(Loc0);
			std::cout<<"[checkPostStmt]---> arg0Namen: "<<name0.str()<<std::endl;
	 */

}
Example #12
0
void MacOSXAPIChecker::checkPreStmt(const CallExpr *CE,
                                    CheckerContext &C) const {
    StringRef Name = C.getCalleeName(CE);
    if (Name.empty())
        return;

    SubChecker SC =
        llvm::StringSwitch<SubChecker>(Name)
        .Cases("dispatch_once", "dispatch_once_f",
               &MacOSXAPIChecker::CheckDispatchOnce)
        .Default(NULL);

    if (SC)
        (this->*SC)(C, CE, Name);
}
bool ExprInspectionChecker::evalCall(const CallExpr *CE,
                                     CheckerContext &C) const {
  // These checks should have no effect on the surrounding environment
  // (globals should not be invalidated, etc), hence the use of evalCall.
  FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))
    .Case("lfort_analyzer_eval", &ExprInspectionChecker::analyzerEval)
    .Case("lfort_analyzer_checkInlined",
          &ExprInspectionChecker::analyzerCheckInlined)
    .Default(0);

  if (!Handler)
    return false;

  (this->*Handler)(CE, C);
  return true;
}
Example #14
0
void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE,
                                            CheckerContext &C) const {
  ProgramStateRef State = C.getState();
  const FunctionDecl *FD = C.getCalleeDecl(CE);
  if (!FD || FD->getKind() != Decl::Function)
    return;

  StringRef funName = C.getCalleeName(FD);

  // If a value has been allocated, add it to the set for tracking.
  unsigned idx = getTrackedFunctionIndex(funName, true);
  if (idx == InvalidIdx)
    return;

  const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
  // If the argument entered as an enclosing function parameter, skip it to
  // avoid false positives.
  if (isEnclosingFunctionParam(ArgExpr) &&
      C.getLocationContext()->getParent() == 0)
    return;

  if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) {
    // If the argument points to something that's not a symbolic region, it
    // can be:
    //  - unknown (cannot reason about it)
    //  - undefined (already reported by other checker)
    //  - constant (null - should not be tracked,
    //              other constant will generate a compiler warning)
    //  - goto (should be reported by other checker)

    // The call return value symbol should stay alive for as long as the
    // allocated value symbol, since our diagnostics depend on the value
    // returned by the call. Ex: Data should only be freed if noErr was
    // returned during allocation.)
    SymbolRef RetStatusSymbol =
      State->getSVal(CE, C.getLocationContext()).getAsSymbol();
    C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol);

    // Track the allocated value in the checker state.
    State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx,
                                                         RetStatusSymbol));
    assert(State);
    C.addTransition(State);
  }
}
Example #15
0
void ObjCContainersChecker::checkPreStmt(const CallExpr *CE,
                                         CheckerContext &C) const {
  StringRef Name = C.getCalleeName(CE);
  if (Name.empty() || CE->getNumArgs() < 2)
    return;

  // Check the array access.
  if (Name.equals("CFArrayGetValueAtIndex")) {
    ProgramStateRef State = C.getState();
    // Retrieve the size.
    // Find out if we saw this array symbol before and have information about
    // it.
    const Expr *ArrayExpr = CE->getArg(0);
    SymbolRef ArraySym = getArraySym(ArrayExpr, C);
    if (!ArraySym)
      return;

    const DefinedSVal *Size = State->get<ArraySizeMap>(ArraySym);

    if (!Size)
      return;

    // Get the index.
    const Expr *IdxExpr = CE->getArg(1);
    SVal IdxVal = State->getSVal(IdxExpr, C.getLocationContext());
    if (IdxVal.isUnknownOrUndef())
      return;
    DefinedSVal Idx = IdxVal.castAs<DefinedSVal>();

    // Now, check if 'Idx in [0, Size-1]'.
    const QualType T = IdxExpr->getType();
    ProgramStateRef StInBound = State->assumeInBound(Idx, *Size, true, T);
    ProgramStateRef StOutBound = State->assumeInBound(Idx, *Size, false, T);
    if (StOutBound && !StInBound) {
      ExplodedNode *N = C.generateSink(StOutBound);
      if (!N)
        return;
      initBugType();
      auto R = llvm::make_unique<BugReport>(*BT, "Index is out of bounds", N);
      R->addRange(IdxExpr->getSourceRange());
      C.emitReport(std::move(R));
      return;
    }
  }
}
Example #16
0
void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
                                       CheckerContext &C) const {
  ProgramStateRef state = C.getState();
  const LocationContext *LCtx = C.getLocationContext();
  StringRef FName = C.getCalleeName(CE);
  if (FName.empty())
    return;

  if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2)
    return;

  if (FName == "pthread_mutex_lock" ||
      FName == "pthread_rwlock_rdlock" ||
      FName == "pthread_rwlock_wrlock")
    AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
                false, PthreadSemantics);
  else if (FName == "lck_mtx_lock" ||
           FName == "lck_rw_lock_exclusive" ||
           FName == "lck_rw_lock_shared") 
    AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
                false, XNUSemantics);
  else if (FName == "pthread_mutex_trylock" ||
           FName == "pthread_rwlock_tryrdlock" ||
           FName == "pthread_rwlock_trywrlock")
    AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
                true, PthreadSemantics);
  else if (FName == "lck_mtx_try_lock" ||
           FName == "lck_rw_try_lock_exclusive" ||
           FName == "lck_rw_try_lock_shared")
    AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
                true, XNUSemantics);
  else if (FName == "pthread_mutex_unlock" ||
           FName == "pthread_rwlock_unlock" ||
           FName == "lck_mtx_unlock" ||
           FName == "lck_rw_done")
    ReleaseLock(C, CE, state->getSVal(CE->getArg(0), LCtx));
  else if (FName == "pthread_mutex_destroy" ||
           FName == "lck_mtx_destroy")
    DestroyLock(C, CE, state->getSVal(CE->getArg(0), LCtx));
  else if (FName == "pthread_mutex_init")
    InitLock(C, CE, state->getSVal(CE->getArg(0), LCtx));
}
void GenericTaintChecker::checkPostStmt(const CallExpr *CE,
                                        CheckerContext &C) const {
  if (!C.getState())
    return;

  StringRef Name = C.getCalleeName(CE);
  
  // Define the attack surface.
  // Set the evaluation function by switching on the callee name.
  FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
    .Case("scanf", &GenericTaintChecker::processScanf)
    .Case("getchar", &GenericTaintChecker::processRetTaint)
    .Default(NULL);

  // If the callee isn't defined, it is not of security concern.
  // Check and evaluate the call.
  if (evalFunction)
    (this->*evalFunction)(CE, C);

}
Example #18
0
void UnixAPIChecker::checkPreStmt(const CallExpr *CE,
                                  CheckerContext &C) const {
  StringRef FName = C.getCalleeName(CE);
  if (FName.empty())
    return;

  SubChecker SC =
    llvm::StringSwitch<SubChecker>(FName)
      .Case("open", &UnixAPIChecker::CheckOpen)
      .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce)
      .Case("calloc", &UnixAPIChecker::CheckCallocZero)
      .Case("malloc", &UnixAPIChecker::CheckMallocZero)
      .Case("realloc", &UnixAPIChecker::CheckReallocZero)
      .Cases("alloca", "__builtin_alloca", &UnixAPIChecker::CheckAllocaZero)
      .Case("valloc", &UnixAPIChecker::CheckVallocZero)
      .Default(NULL);

  if (SC)
    (this->*SC)(C, CE);
}
bool GenericTaintChecker::checkPre(const CallExpr *CE, CheckerContext &C) const{

  if (checkUncontrolledFormatString(CE, C))
    return true;

  const FunctionDecl *FDecl = C.getCalleeDecl(CE);
  if (!FDecl || FDecl->getKind() != Decl::Function)
    return false;

  StringRef Name = C.getCalleeName(FDecl);
  if (Name.empty())
    return false;

  if (checkSystemCall(CE, Name, C))
    return true;

  if (checkTaintedBufferSize(CE, FDecl, C))
    return true;

  return false;
}
Example #20
0
void ObjCContainersChecker::checkPostStmt(const CallExpr *CE,
                                          CheckerContext &C) const {
  StringRef Name = C.getCalleeName(CE);
  if (Name.empty() || CE->getNumArgs() < 1)
    return;

  // Add array size information to the state.
  if (Name.equals("CFArrayCreate")) {
    if (CE->getNumArgs() < 3)
      return;
    // Note, we can visit the Create method in the post-visit because
    // the CFIndex parameter is passed in by value and will not be invalidated
    // by the call.
    addSizeInfo(CE, CE->getArg(2), C);
    return;
  }

  if (Name.equals("CFArrayGetCount")) {
    addSizeInfo(CE->getArg(0), CE, C);
    return;
  }
}
void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
                                           const Stmt *StoreE,
                                           CheckerContext &C) const {
  if (!val.isUndef())
    return;

  // Do not report assignments of uninitialized values inside swap functions.
  // This should allow to swap partially uninitialized structs
  // (radar://14129997)
  if (const FunctionDecl *EnclosingFunctionDecl =
      dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()))
    if (C.getCalleeName(EnclosingFunctionDecl) == "swap")
      return;

  ExplodedNode *N = C.generateErrorNode();

  if (!N)
    return;

  static const char *const DefaultMsg =
      "Assigned value is garbage or undefined";
  if (!BT)
    BT.reset(new BuiltinBug(this, DefaultMsg));

  // Generate a report for this bug.
  llvm::SmallString<128> Str;
  llvm::raw_svector_ostream OS(Str);

  const Expr *ex = nullptr;

  while (StoreE) {
    if (const UnaryOperator *U = dyn_cast<UnaryOperator>(StoreE)) {
      OS << "The expression is an uninitialized value. "
            "The computed value will also be garbage";

      ex = U->getSubExpr();
      break;
    }

    if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) {
      if (B->isCompoundAssignmentOp()) {
        if (C.getSVal(B->getLHS()).isUndef()) {
          OS << "The left expression of the compound assignment is an "
                "uninitialized value. The computed value will also be garbage";
          ex = B->getLHS();
          break;
        }
      }

      ex = B->getRHS();
      break;
    }

    if (const DeclStmt *DS = dyn_cast<DeclStmt>(StoreE)) {
      const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
      ex = VD->getInit();
    }

    if (const auto *CD =
            dyn_cast<CXXConstructorDecl>(C.getStackFrame()->getDecl())) {
      if (CD->isImplicit()) {
        for (auto I : CD->inits()) {
          if (I->getInit()->IgnoreImpCasts() == StoreE) {
            OS << "Value assigned to field '" << I->getMember()->getName()
               << "' in implicit constructor is garbage or undefined";
            break;
          }
        }
      }
    }

    break;
  }

  if (OS.str().empty())
    OS << DefaultMsg;

  auto R = llvm::make_unique<BugReport>(*BT, OS.str(), N);
  if (ex) {
    R->addRange(ex->getSourceRange());
    bugreporter::trackExpressionValue(N, ex, *R);
  }
  C.emitReport(std::move(R));
}
Example #22
0
void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
                                           CheckerContext &C) const {
  unsigned idx = InvalidIdx;
  ProgramStateRef State = C.getState();

  const FunctionDecl *FD = C.getCalleeDecl(CE);
  if (!FD || FD->getKind() != Decl::Function)
    return;
  
  StringRef funName = C.getCalleeName(FD);
  if (funName.empty())
    return;

  // If it is a call to an allocator function, it could be a double allocation.
  idx = getTrackedFunctionIndex(funName, true);
  if (idx != InvalidIdx) {
    const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
    if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C))
      if (const AllocationState *AS = State->get<AllocatedData>(V)) {
        if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) {
          // Remove the value from the state. The new symbol will be added for
          // tracking when the second allocator is processed in checkPostStmt().
          State = State->remove<AllocatedData>(V);
          ExplodedNode *N = C.addTransition(State);
          if (!N)
            return;
          initBugType();
          SmallString<128> sbuf;
          llvm::raw_svector_ostream os(sbuf);
          unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
          os << "Allocated data should be released before another call to "
              << "the allocator: missing a call to '"
              << FunctionsToTrack[DIdx].Name
              << "'.";
          BugReport *Report = new BugReport(*BT, os.str(), N);
          Report->addVisitor(new SecKeychainBugVisitor(V));
          Report->addRange(ArgExpr->getSourceRange());
          Report->markInteresting(AS->Region);
          C.emitReport(Report);
        }
      }
    return;
  }

  // Is it a call to one of deallocator functions?
  idx = getTrackedFunctionIndex(funName, false);
  if (idx == InvalidIdx)
    return;

  // Check the argument to the deallocator.
  const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
  SVal ArgSVal = State->getSVal(ArgExpr, C.getLocationContext());

  // Undef is reported by another checker.
  if (ArgSVal.isUndef())
    return;

  SymbolRef ArgSM = ArgSVal.getAsLocSymbol();

  // If the argument is coming from the heap, globals, or unknown, do not
  // report it.
  bool RegionArgIsBad = false;
  if (!ArgSM) {
    if (!isBadDeallocationArgument(ArgSVal.getAsRegion()))
      return;
    RegionArgIsBad = true;
  }

  // Is the argument to the call being tracked?
  const AllocationState *AS = State->get<AllocatedData>(ArgSM);
  if (!AS && FunctionsToTrack[idx].Kind != ValidAPI) {
    return;
  }
  // If trying to free data which has not been allocated yet, report as a bug.
  // TODO: We might want a more precise diagnostic for double free
  // (that would involve tracking all the freed symbols in the checker state).
  if (!AS || RegionArgIsBad) {
    // It is possible that this is a false positive - the argument might
    // have entered as an enclosing function parameter.
    if (isEnclosingFunctionParam(ArgExpr))
      return;

    ExplodedNode *N = C.addTransition(State);
    if (!N)
      return;
    initBugType();
    BugReport *Report = new BugReport(*BT,
        "Trying to free data which has not been allocated.", N);
    Report->addRange(ArgExpr->getSourceRange());
    if (AS)
      Report->markInteresting(AS->Region);
    C.emitReport(Report);
    return;
  }

  // Process functions which might deallocate.
  if (FunctionsToTrack[idx].Kind == PossibleAPI) {

    if (funName == "CFStringCreateWithBytesNoCopy") {
      const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts();
      // NULL ~ default deallocator, so warn.
      if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(),
          Expr::NPC_ValueDependentIsNotNull)) {
        const AllocationPair AP = std::make_pair(ArgSM, AS);
        generateDeallocatorMismatchReport(AP, ArgExpr, C);
        return;
      }
      // One of the default allocators, so warn.
      if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) {
        StringRef DeallocatorName = DE->getFoundDecl()->getName();
        if (DeallocatorName == "kCFAllocatorDefault" ||
            DeallocatorName == "kCFAllocatorSystemDefault" ||
            DeallocatorName == "kCFAllocatorMalloc") {
          const AllocationPair AP = std::make_pair(ArgSM, AS);
          generateDeallocatorMismatchReport(AP, ArgExpr, C);
          return;
        }
        // If kCFAllocatorNull, which does not deallocate, we still have to
        // find the deallocator.
        if (DE->getFoundDecl()->getName() == "kCFAllocatorNull")
          return;
      }
      // In all other cases, assume the user supplied a correct deallocator
      // that will free memory so stop tracking.
      State = State->remove<AllocatedData>(ArgSM);
      C.addTransition(State);
      return;
    }

    llvm_unreachable("We know of no other possible APIs.");
  }

  // The call is deallocating a value we previously allocated, so remove it
  // from the next state.
  State = State->remove<AllocatedData>(ArgSM);

  // Check if the proper deallocator is used.
  unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
  if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) {
    const AllocationPair AP = std::make_pair(ArgSM, AS);
    generateDeallocatorMismatchReport(AP, ArgExpr, C);
    return;
  }

  // If the buffer can be null and the return status can be an error,
  // report a bad call to free.
  if (State->assume(ArgSVal.castAs<DefinedSVal>(), false) &&
      !definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) {
    ExplodedNode *N = C.addTransition(State);
    if (!N)
      return;
    initBugType();
    BugReport *Report = new BugReport(*BT,
        "Only call free if a valid (non-NULL) buffer was returned.", N);
    Report->addVisitor(new SecKeychainBugVisitor(ArgSM));
    Report->addRange(ArgExpr->getSourceRange());
    Report->markInteresting(AS->Region);
    C.emitReport(Report);
    return;
  }

  C.addTransition(State);
}