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 ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const { const Expr *RetE = RS->getRetValue(); if (!RetE) return; SVal RetVal = C.getSVal(RetE); const StackFrameContext *SFC = C.getStackFrame(); QualType RT = CallEvent::getDeclaredResultType(SFC->getDecl()); if (RetVal.isUndef()) { // "return;" is modeled to evaluate to an UndefinedVal. Allow UndefinedVal // to be returned in functions returning void to support this pattern: // void foo() { // return; // } // void test() { // return foo(); // } if (RT.isNull() || !RT->isVoidType()) emitUndef(C, RetE); return; } if (RT.isNull()) return; if (RT->isReferenceType()) { checkReference(C, RetE, RetVal.castAs<DefinedOrUnknownSVal>()); 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.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); }
bool TestAfterDivZeroChecker::hasDivZeroMap(SVal Var, const CheckerContext &C) const { SymbolRef SR = Var.getAsSymbol(); if (!SR) return false; ZeroState ZS(SR, C.getBlockID(), C.getStackFrame()); return C.getState()->contains<DivZeroMap>(ZS); }
void TestAfterDivZeroChecker::setDivZeroMap(SVal Var, CheckerContext &C) const { SymbolRef SR = Var.getAsSymbol(); if (!SR) return; ProgramStateRef State = C.getState(); State = State->add<DivZeroMap>(ZeroState(SR, C.getBlockID(), C.getStackFrame())); C.addTransition(State); }
static Optional<nonloc::LazyCompoundVal> getObjectVal(const CXXConstructorDecl *CtorDecl, CheckerContext &Context) { Loc ThisLoc = Context.getSValBuilder().getCXXThis(CtorDecl->getParent(), Context.getStackFrame()); // Getting the value for 'this'. SVal This = Context.getState()->getSVal(ThisLoc); // Getting the value for '*this'. SVal Object = Context.getState()->getSVal(This.castAs<Loc>()); return Object.getAs<nonloc::LazyCompoundVal>(); }
void TestAfterDivZeroChecker::reportBug(SVal Val, CheckerContext &C) const { if (ExplodedNode *N = C.generateSink(C.getState())) { if (!DivZeroBug) DivZeroBug.reset(new BuiltinBug(this, "Division by zero")); BugReport *R = new BugReport(*DivZeroBug, "Value being compared against zero has " "already been used for division", N); R->addVisitor(new DivisionBRVisitor(Val.getAsSymbol(), C.getStackFrame())); C.emitReport(R); } }
void TestAfterDivZeroChecker::reportBug(SVal Val, CheckerContext &C) const { if (ExplodedNode *N = C.generateSink(C.getState())) { if (!DivZeroBug) DivZeroBug.reset(new BuiltinBug(this, "Division by zero")); auto R = llvm::make_unique<BugReport>( *DivZeroBug, "Value being compared against zero has already been used " "for division", N); R->addVisitor(llvm::make_unique<DivisionBRVisitor>(Val.getAsSymbol(), C.getStackFrame())); C.emitReport(std::move(R)); } }
void TestAfterDivZeroChecker::checkEndFunction(CheckerContext &C) const { ProgramStateRef State = C.getState(); DivZeroMapTy DivZeroes = State->get<DivZeroMap>(); if (DivZeroes.isEmpty()) return; DivZeroMapTy::Factory &F = State->get_context<DivZeroMap>(); for (llvm::ImmutableSet<ZeroState>::iterator I = DivZeroes.begin(), E = DivZeroes.end(); I != E; ++I) { ZeroState ZS = *I; if (ZS.getStackFrameContext() == C.getStackFrame()) DivZeroes = F.remove(DivZeroes, ZS); } C.addTransition(State->set<DivZeroMap>(DivZeroes)); }
void DanglingDelegateChecker::checkEndFunction(CheckerContext &context) const { // dealloc implicitly releases ivars only in ARC-mode if (!context.getLangOpts().ObjCAutoRefCount) { return; } const ObjCImplFacts *facts = getCurrentFacts(getCurrentTopClassInterface(context)); if (!facts) { return; } const ObjCMethodDecl *decl = dyn_cast_or_null<ObjCMethodDecl>(context.getStackFrame()->getDecl()); if (!decl || decl->getSelector().getAsString() != "dealloc") { return; } const std::function<void(StringRef)> emitBug([this, decl, &context](StringRef str) { BugReport *report = new BugReport(*_bugType, str, context.getPredecessor()); report->addRange(decl->getSourceRange()); context.emitReport(report); }); ProgramStateRef state = context.getState(); // Verify that all the dangerous properties have been cleared const IvarDynamicState emptyIds; for (auto it = facts->_ivarFactsMap.begin(), end = facts->_ivarFactsMap.end(); it != end; it++) { const ObjCIvarDecl *ivarDecl = it->first; const StringSet &dangerousProperties = it->second._mayStoreSelfInUnsafeProperty; const IvarDynamicState *ids = state->get<IvarMap>(ivarDecl); if (!ids) { ids = &emptyIds; } const StringSet &clearedProperties = ids->_assignPropertyWasCleared; verifyAndReportDangerousProperties(dangerousProperties, clearedProperties, ivarDecl->getNameAsString(), ivarDecl->getType(), "ARC-generated code", emitBug); } }
void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const { const Expr *RetE = RS->getRetValue(); if (!RetE) return; SVal RetVal = C.getSVal(RetE); const StackFrameContext *SFC = C.getStackFrame(); QualType RT = CallEvent::getDeclaredResultType(SFC->getDecl()); if (RetVal.isUndef()) { // "return;" is modeled to evaluate to an UndefinedVal. Allow UndefinedVal // to be returned in functions returning void to support this pattern: // void foo() { // return; // } // void test() { // return foo(); // } if (!RT.isNull() && RT->isVoidType()) return; // Not all blocks have explicitly-specified return types; if the return type // is not available, but the return value expression has 'void' type, assume // Sema already checked it. if (RT.isNull() && isa<BlockDecl>(SFC->getDecl()) && RetE->getType()->isVoidType()) return; emitUndef(C, RetE); return; } if (RT.isNull()) return; if (RT->isReferenceType()) { checkReference(C, RetE, RetVal.castAs<DefinedOrUnknownSVal>()); return; } }
void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const { const Expr *RetE = RS->getRetValue(); if (!RetE) return; if (!C.getState()->getSVal(RetE, C.getLocationContext()).isUndef()) return; // "return;" is modeled to evaluate to an UndefinedValue. Allow UndefinedValue // to be returned in functions returning void to support the following pattern: // void foo() { // return; // } // void test() { // return foo(); // } const StackFrameContext *SFC = C.getStackFrame(); QualType RT = CallEvent::getDeclaredResultType(SFC->getDecl()); if (!RT.isNull() && RT->isSpecificBuiltinType(BuiltinType::Void)) return; ExplodedNode *N = C.generateSink(); if (!N) return; if (!BT) BT.reset(new BuiltinBug("Garbage return value", "Undefined or garbage value returned to caller")); BugReport *report = new BugReport(*BT, BT->getDescription(), N); report->addRange(RetE->getSourceRange()); bugreporter::trackNullOrUndefValue(N, RetE, *report); C.emitReport(report); }
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)); }
void UninitializedObjectChecker::checkEndFunction( const ReturnStmt *RS, CheckerContext &Context) const { const auto *CtorDecl = dyn_cast_or_null<CXXConstructorDecl>( Context.getLocationContext()->getDecl()); if (!CtorDecl) return; if (!CtorDecl->isUserProvided()) return; if (CtorDecl->getParent()->isUnion()) return; // This avoids essentially the same error being reported multiple times. if (willObjectBeAnalyzedLater(CtorDecl, Context)) return; Optional<nonloc::LazyCompoundVal> Object = getObjectVal(CtorDecl, Context); if (!Object) return; FindUninitializedFields F(Context.getState(), Object->getRegion(), CheckPointeeInitialization); const UninitFieldMap &UninitFields = F.getUninitFields(); if (UninitFields.empty()) return; // In non-pedantic mode, if Object's region doesn't contain a single // initialized field, we'll assume that Object was intentionally left // uninitialized. if (!IsPedantic && !F.isAnyFieldInitialized()) return; // There are uninitialized fields in the record. ExplodedNode *Node = Context.generateNonFatalErrorNode(Context.getState()); if (!Node) return; PathDiagnosticLocation LocUsedForUniqueing; const Stmt *CallSite = Context.getStackFrame()->getCallSite(); if (CallSite) LocUsedForUniqueing = PathDiagnosticLocation::createBegin( CallSite, Context.getSourceManager(), Node->getLocationContext()); // For Plist consumers that don't support notes just yet, we'll convert notes // to warnings. if (ShouldConvertNotesToWarnings) { for (const auto &Pair : UninitFields) { auto Report = llvm::make_unique<BugReport>( *BT_uninitField, Pair.second, Node, LocUsedForUniqueing, Node->getLocationContext()->getDecl()); Context.emitReport(std::move(Report)); } return; } SmallString<100> WarningBuf; llvm::raw_svector_ostream WarningOS(WarningBuf); WarningOS << UninitFields.size() << " uninitialized field" << (UninitFields.size() == 1 ? "" : "s") << " at the end of the constructor call"; auto Report = llvm::make_unique<BugReport>( *BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing, Node->getLocationContext()->getDecl()); for (const auto &Pair : UninitFields) { Report->addNote(Pair.second, PathDiagnosticLocation::create(Pair.first->getDecl(), Context.getSourceManager())); } Context.emitReport(std::move(Report)); }