/// Insert a diagnostic piece at function exit
/// if a function parameter is annotated as "os_consumed",
/// but it does not actually consume the reference.
static std::shared_ptr<PathDiagnosticEventPiece>
annotateConsumedSummaryMismatch(const ExplodedNode *N,
                                CallExitBegin &CallExitLoc,
                                const SourceManager &SM,
                                CallEventManager &CEMgr) {

  const ExplodedNode *CN = getCalleeNode(N);
  if (!CN)
    return nullptr;

  CallEventRef<> Call = CEMgr.getCaller(N->getStackFrame(), N->getState());

  std::string sbuf;
  llvm::raw_string_ostream os(sbuf);
  ArrayRef<const ParmVarDecl *> Parameters = Call->parameters();
  for (unsigned I=0; I < Call->getNumArgs() && I < Parameters.size(); ++I) {
    const ParmVarDecl *PVD = Parameters[I];

    if (!PVD->hasAttr<OSConsumedAttr>())
      continue;

    if (SymbolRef SR = Call->getArgSVal(I).getAsLocSymbol()) {
      const RefVal *CountBeforeCall = getRefBinding(CN->getState(), SR);
      const RefVal *CountAtExit = getRefBinding(N->getState(), SR);

      if (!CountBeforeCall || !CountAtExit)
        continue;

      unsigned CountBefore = CountBeforeCall->getCount();
      unsigned CountAfter = CountAtExit->getCount();

      bool AsExpected = CountBefore > 0 && CountAfter == CountBefore - 1;
      if (!AsExpected) {
        os << "Parameter '";
        PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(),
                                  /*Qualified=*/false);
        os << "' is marked as consuming, but the function did not consume "
           << "the reference\n";
      }
    }
  }

  if (os.str().empty())
    return nullptr;

  // FIXME: remove the code duplication with NoStoreFuncVisitor.
  PathDiagnosticLocation L;
  if (const ReturnStmt *RS = CallExitLoc.getReturnStmt()) {
    L = PathDiagnosticLocation::createBegin(RS, SM, N->getLocationContext());
  } else {
    L = PathDiagnosticLocation(
        Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), SM);
  }

  return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
}
Example #2
0
void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred,
                                 const CallEvent &CallTemplate,
                                 const EvalCallOptions &CallOpts) {
  // Make sure we have the most recent state attached to the call.
  ProgramStateRef State = Pred->getState();
  CallEventRef<> Call = CallTemplate.cloneWithState(State);

  // Special-case trivial assignment operators.
  if (isTrivialObjectAssignment(*Call)) {
    performTrivialCopy(Bldr, Pred, *Call);
    return;
  }

  // Try to inline the call.
  // The origin expression here is just used as a kind of checksum;
  // this should still be safe even for CallEvents that don't come from exprs.
  const Expr *E = Call->getOriginExpr();

  ProgramStateRef InlinedFailedState = getInlineFailedState(State, E);
  if (InlinedFailedState) {
    // If we already tried once and failed, make sure we don't retry later.
    State = InlinedFailedState;
  } else {
    RuntimeDefinition RD = Call->getRuntimeDefinition();
    const Decl *D = RD.getDecl();
    if (shouldInlineCall(*Call, D, Pred, CallOpts)) {
      if (RD.mayHaveOtherDefinitions()) {
        AnalyzerOptions &Options = getAnalysisManager().options;

        // Explore with and without inlining the call.
        if (Options.getIPAMode() == IPAK_DynamicDispatchBifurcate) {
          BifurcateCall(RD.getDispatchRegion(), *Call, D, Bldr, Pred);
          return;
        }

        // Don't inline if we're not in any dynamic dispatch mode.
        if (Options.getIPAMode() != IPAK_DynamicDispatch) {
          conservativeEvalCall(*Call, Bldr, Pred, State);
          return;
        }
      }

      // We are not bifurcating and we do have a Decl, so just inline.
      if (inlineCall(*Call, D, Bldr, Pred, State))
        return;
    }
  }

  // If we can't inline it, handle the return value and invalidate the regions.
  conservativeEvalCall(*Call, Bldr, Pred, State);
}