181ad6265SDimitry Andric //=== ErrnoChecker.cpp ------------------------------------------*- C++ -*-===// 281ad6265SDimitry Andric // 381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 681ad6265SDimitry Andric // 781ad6265SDimitry Andric //===----------------------------------------------------------------------===// 881ad6265SDimitry Andric // 981ad6265SDimitry Andric // This defines an "errno checker" that can detect some invalid use of the 1081ad6265SDimitry Andric // system-defined value 'errno'. This checker works together with the 1181ad6265SDimitry Andric // ErrnoModeling checker and other checkers like StdCLibraryFunctions. 1281ad6265SDimitry Andric // 1381ad6265SDimitry Andric //===----------------------------------------------------------------------===// 1481ad6265SDimitry Andric 1581ad6265SDimitry Andric #include "ErrnoModeling.h" 1681ad6265SDimitry Andric #include "clang/AST/ParentMapContext.h" 1781ad6265SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 1881ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 1981ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h" 20*0fca6ea1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 2181ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 2281ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 2381ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" 2481ad6265SDimitry Andric #include "llvm/ADT/STLExtras.h" 25bdd1243dSDimitry Andric #include <optional> 2681ad6265SDimitry Andric 2781ad6265SDimitry Andric using namespace clang; 2881ad6265SDimitry Andric using namespace ento; 2981ad6265SDimitry Andric using namespace errno_modeling; 3081ad6265SDimitry Andric 3181ad6265SDimitry Andric namespace { 3281ad6265SDimitry Andric 3381ad6265SDimitry Andric class ErrnoChecker 3481ad6265SDimitry Andric : public Checker<check::Location, check::PreCall, check::RegionChanges> { 3581ad6265SDimitry Andric public: 3681ad6265SDimitry Andric void checkLocation(SVal Loc, bool IsLoad, const Stmt *S, 3781ad6265SDimitry Andric CheckerContext &) const; 3881ad6265SDimitry Andric void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 3981ad6265SDimitry Andric ProgramStateRef 4081ad6265SDimitry Andric checkRegionChanges(ProgramStateRef State, 4181ad6265SDimitry Andric const InvalidatedSymbols *Invalidated, 4281ad6265SDimitry Andric ArrayRef<const MemRegion *> ExplicitRegions, 4381ad6265SDimitry Andric ArrayRef<const MemRegion *> Regions, 4481ad6265SDimitry Andric const LocationContext *LCtx, const CallEvent *Call) const; 4581ad6265SDimitry Andric void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const; 4681ad6265SDimitry Andric 4781ad6265SDimitry Andric /// Indicates if a read (load) of \c errno is allowed in a non-condition part 4881ad6265SDimitry Andric /// of \c if, \c switch, loop and conditional statements when the errno 4981ad6265SDimitry Andric /// value may be undefined. 5081ad6265SDimitry Andric bool AllowErrnoReadOutsideConditions = true; 5181ad6265SDimitry Andric 5281ad6265SDimitry Andric private: 5381ad6265SDimitry Andric void generateErrnoNotCheckedBug(CheckerContext &C, ProgramStateRef State, 5481ad6265SDimitry Andric const MemRegion *ErrnoRegion, 5581ad6265SDimitry Andric const CallEvent *CallMayChangeErrno) const; 5681ad6265SDimitry Andric 5781ad6265SDimitry Andric BugType BT_InvalidErrnoRead{this, "Value of 'errno' could be undefined", 5881ad6265SDimitry Andric "Error handling"}; 5981ad6265SDimitry Andric BugType BT_ErrnoNotChecked{this, "Value of 'errno' was not checked", 6081ad6265SDimitry Andric "Error handling"}; 6181ad6265SDimitry Andric }; 6281ad6265SDimitry Andric 6381ad6265SDimitry Andric } // namespace 6481ad6265SDimitry Andric 6581ad6265SDimitry Andric static ProgramStateRef setErrnoStateIrrelevant(ProgramStateRef State) { 6681ad6265SDimitry Andric return setErrnoState(State, Irrelevant); 6781ad6265SDimitry Andric } 6881ad6265SDimitry Andric 6981ad6265SDimitry Andric /// Check if a statement (expression) or an ancestor of it is in a condition 7081ad6265SDimitry Andric /// part of a (conditional, loop, switch) statement. 7181ad6265SDimitry Andric static bool isInCondition(const Stmt *S, CheckerContext &C) { 7281ad6265SDimitry Andric ParentMapContext &ParentCtx = C.getASTContext().getParentMapContext(); 7381ad6265SDimitry Andric bool CondFound = false; 7481ad6265SDimitry Andric while (S && !CondFound) { 7581ad6265SDimitry Andric const DynTypedNodeList Parents = ParentCtx.getParents(*S); 7681ad6265SDimitry Andric if (Parents.empty()) 7781ad6265SDimitry Andric break; 7881ad6265SDimitry Andric const auto *ParentS = Parents[0].get<Stmt>(); 7981ad6265SDimitry Andric if (!ParentS || isa<CallExpr>(ParentS)) 8081ad6265SDimitry Andric break; 8181ad6265SDimitry Andric switch (ParentS->getStmtClass()) { 8281ad6265SDimitry Andric case Expr::IfStmtClass: 8381ad6265SDimitry Andric CondFound = (S == cast<IfStmt>(ParentS)->getCond()); 8481ad6265SDimitry Andric break; 8581ad6265SDimitry Andric case Expr::ForStmtClass: 8681ad6265SDimitry Andric CondFound = (S == cast<ForStmt>(ParentS)->getCond()); 8781ad6265SDimitry Andric break; 8881ad6265SDimitry Andric case Expr::DoStmtClass: 8981ad6265SDimitry Andric CondFound = (S == cast<DoStmt>(ParentS)->getCond()); 9081ad6265SDimitry Andric break; 9181ad6265SDimitry Andric case Expr::WhileStmtClass: 9281ad6265SDimitry Andric CondFound = (S == cast<WhileStmt>(ParentS)->getCond()); 9381ad6265SDimitry Andric break; 9481ad6265SDimitry Andric case Expr::SwitchStmtClass: 9581ad6265SDimitry Andric CondFound = (S == cast<SwitchStmt>(ParentS)->getCond()); 9681ad6265SDimitry Andric break; 9781ad6265SDimitry Andric case Expr::ConditionalOperatorClass: 9881ad6265SDimitry Andric CondFound = (S == cast<ConditionalOperator>(ParentS)->getCond()); 9981ad6265SDimitry Andric break; 10081ad6265SDimitry Andric case Expr::BinaryConditionalOperatorClass: 10181ad6265SDimitry Andric CondFound = (S == cast<BinaryConditionalOperator>(ParentS)->getCommon()); 10281ad6265SDimitry Andric break; 10381ad6265SDimitry Andric default: 10481ad6265SDimitry Andric break; 10581ad6265SDimitry Andric } 10681ad6265SDimitry Andric S = ParentS; 10781ad6265SDimitry Andric } 10881ad6265SDimitry Andric return CondFound; 10981ad6265SDimitry Andric } 11081ad6265SDimitry Andric 11181ad6265SDimitry Andric void ErrnoChecker::generateErrnoNotCheckedBug( 11281ad6265SDimitry Andric CheckerContext &C, ProgramStateRef State, const MemRegion *ErrnoRegion, 11381ad6265SDimitry Andric const CallEvent *CallMayChangeErrno) const { 11481ad6265SDimitry Andric if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { 11581ad6265SDimitry Andric SmallString<100> StrBuf; 11681ad6265SDimitry Andric llvm::raw_svector_ostream OS(StrBuf); 11781ad6265SDimitry Andric if (CallMayChangeErrno) { 11881ad6265SDimitry Andric OS << "Value of 'errno' was not checked and may be overwritten by " 11981ad6265SDimitry Andric "function '"; 12081ad6265SDimitry Andric const auto *CallD = 12181ad6265SDimitry Andric dyn_cast_or_null<FunctionDecl>(CallMayChangeErrno->getDecl()); 12281ad6265SDimitry Andric assert(CallD && CallD->getIdentifier()); 12381ad6265SDimitry Andric OS << CallD->getIdentifier()->getName() << "'"; 12481ad6265SDimitry Andric } else { 12581ad6265SDimitry Andric OS << "Value of 'errno' was not checked and is overwritten here"; 12681ad6265SDimitry Andric } 12781ad6265SDimitry Andric auto BR = std::make_unique<PathSensitiveBugReport>(BT_ErrnoNotChecked, 12881ad6265SDimitry Andric OS.str(), N); 12981ad6265SDimitry Andric BR->markInteresting(ErrnoRegion); 13081ad6265SDimitry Andric C.emitReport(std::move(BR)); 13181ad6265SDimitry Andric } 13281ad6265SDimitry Andric } 13381ad6265SDimitry Andric 13481ad6265SDimitry Andric void ErrnoChecker::checkLocation(SVal Loc, bool IsLoad, const Stmt *S, 13581ad6265SDimitry Andric CheckerContext &C) const { 136bdd1243dSDimitry Andric std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState()); 13781ad6265SDimitry Andric if (!ErrnoLoc) 13881ad6265SDimitry Andric return; 13981ad6265SDimitry Andric 14081ad6265SDimitry Andric auto L = Loc.getAs<ento::Loc>(); 14181ad6265SDimitry Andric if (!L || *ErrnoLoc != *L) 14281ad6265SDimitry Andric return; 14381ad6265SDimitry Andric 14481ad6265SDimitry Andric ProgramStateRef State = C.getState(); 14581ad6265SDimitry Andric ErrnoCheckState EState = getErrnoState(State); 14681ad6265SDimitry Andric 14781ad6265SDimitry Andric if (IsLoad) { 14881ad6265SDimitry Andric switch (EState) { 14981ad6265SDimitry Andric case MustNotBeChecked: 15081ad6265SDimitry Andric // Read of 'errno' when it may have undefined value. 15181ad6265SDimitry Andric if (!AllowErrnoReadOutsideConditions || isInCondition(S, C)) { 15281ad6265SDimitry Andric if (ExplodedNode *N = C.generateErrorNode()) { 15381ad6265SDimitry Andric auto BR = std::make_unique<PathSensitiveBugReport>( 15481ad6265SDimitry Andric BT_InvalidErrnoRead, 15581ad6265SDimitry Andric "An undefined value may be read from 'errno'", N); 15681ad6265SDimitry Andric BR->markInteresting(ErrnoLoc->getAsRegion()); 15781ad6265SDimitry Andric C.emitReport(std::move(BR)); 15881ad6265SDimitry Andric } 15981ad6265SDimitry Andric } 16081ad6265SDimitry Andric break; 16181ad6265SDimitry Andric case MustBeChecked: 16281ad6265SDimitry Andric // 'errno' has to be checked. A load is required for this, with no more 16381ad6265SDimitry Andric // information we can assume that it is checked somehow. 16481ad6265SDimitry Andric // After this place 'errno' is allowed to be read and written. 16581ad6265SDimitry Andric State = setErrnoStateIrrelevant(State); 16681ad6265SDimitry Andric C.addTransition(State); 16781ad6265SDimitry Andric break; 16881ad6265SDimitry Andric default: 16981ad6265SDimitry Andric break; 17081ad6265SDimitry Andric } 17181ad6265SDimitry Andric } else { 17281ad6265SDimitry Andric switch (EState) { 17381ad6265SDimitry Andric case MustBeChecked: 17481ad6265SDimitry Andric // 'errno' is overwritten without a read before but it should have been 17581ad6265SDimitry Andric // checked. 17681ad6265SDimitry Andric generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(State), 17781ad6265SDimitry Andric ErrnoLoc->getAsRegion(), nullptr); 17881ad6265SDimitry Andric break; 17981ad6265SDimitry Andric case MustNotBeChecked: 18081ad6265SDimitry Andric // Write to 'errno' when it is not allowed to be read. 18181ad6265SDimitry Andric // After this place 'errno' is allowed to be read and written. 18281ad6265SDimitry Andric State = setErrnoStateIrrelevant(State); 18381ad6265SDimitry Andric C.addTransition(State); 18481ad6265SDimitry Andric break; 18581ad6265SDimitry Andric default: 18681ad6265SDimitry Andric break; 18781ad6265SDimitry Andric } 18881ad6265SDimitry Andric } 18981ad6265SDimitry Andric } 19081ad6265SDimitry Andric 19181ad6265SDimitry Andric void ErrnoChecker::checkPreCall(const CallEvent &Call, 19281ad6265SDimitry Andric CheckerContext &C) const { 19381ad6265SDimitry Andric const auto *CallF = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 19481ad6265SDimitry Andric if (!CallF) 19581ad6265SDimitry Andric return; 19681ad6265SDimitry Andric 19781ad6265SDimitry Andric CallF = CallF->getCanonicalDecl(); 19881ad6265SDimitry Andric // If 'errno' must be checked, it should be done as soon as possible, and 19981ad6265SDimitry Andric // before any other call to a system function (something in a system header). 20081ad6265SDimitry Andric // To avoid use of a long list of functions that may change 'errno' 20181ad6265SDimitry Andric // (which may be different with standard library versions) assume that any 20281ad6265SDimitry Andric // function can change it. 20381ad6265SDimitry Andric // A list of special functions can be used that are allowed here without 20481ad6265SDimitry Andric // generation of diagnostic. For now the only such case is 'errno' itself. 20581ad6265SDimitry Andric // Probably 'strerror'? 20681ad6265SDimitry Andric if (CallF->isExternC() && CallF->isGlobal() && 20781ad6265SDimitry Andric C.getSourceManager().isInSystemHeader(CallF->getLocation()) && 208*0fca6ea1SDimitry Andric !isErrnoLocationCall(Call)) { 20981ad6265SDimitry Andric if (getErrnoState(C.getState()) == MustBeChecked) { 210bdd1243dSDimitry Andric std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState()); 21181ad6265SDimitry Andric assert(ErrnoLoc && "ErrnoLoc should exist if an errno state is set."); 21281ad6265SDimitry Andric generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(C.getState()), 21381ad6265SDimitry Andric ErrnoLoc->getAsRegion(), &Call); 21481ad6265SDimitry Andric } 21581ad6265SDimitry Andric } 21681ad6265SDimitry Andric } 21781ad6265SDimitry Andric 21881ad6265SDimitry Andric ProgramStateRef ErrnoChecker::checkRegionChanges( 21981ad6265SDimitry Andric ProgramStateRef State, const InvalidatedSymbols *Invalidated, 22081ad6265SDimitry Andric ArrayRef<const MemRegion *> ExplicitRegions, 22181ad6265SDimitry Andric ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx, 22281ad6265SDimitry Andric const CallEvent *Call) const { 223bdd1243dSDimitry Andric std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(State); 22481ad6265SDimitry Andric if (!ErrnoLoc) 22581ad6265SDimitry Andric return State; 22681ad6265SDimitry Andric const MemRegion *ErrnoRegion = ErrnoLoc->getAsRegion(); 22781ad6265SDimitry Andric 22881ad6265SDimitry Andric // If 'errno' is invalidated we can not know if it is checked or written into, 22981ad6265SDimitry Andric // allow read and write without bug reports. 23081ad6265SDimitry Andric if (llvm::is_contained(Regions, ErrnoRegion)) 231bdd1243dSDimitry Andric return clearErrnoState(State); 23281ad6265SDimitry Andric 23381ad6265SDimitry Andric // Always reset errno state when the system memory space is invalidated. 23481ad6265SDimitry Andric // The ErrnoRegion is not always found in the list in this case. 23581ad6265SDimitry Andric if (llvm::is_contained(Regions, ErrnoRegion->getMemorySpace())) 236bdd1243dSDimitry Andric return clearErrnoState(State); 23781ad6265SDimitry Andric 23881ad6265SDimitry Andric return State; 23981ad6265SDimitry Andric } 24081ad6265SDimitry Andric 24181ad6265SDimitry Andric void ento::registerErrnoChecker(CheckerManager &mgr) { 24281ad6265SDimitry Andric const AnalyzerOptions &Opts = mgr.getAnalyzerOptions(); 24381ad6265SDimitry Andric auto *Checker = mgr.registerChecker<ErrnoChecker>(); 24481ad6265SDimitry Andric Checker->AllowErrnoReadOutsideConditions = Opts.getCheckerBooleanOption( 24581ad6265SDimitry Andric Checker, "AllowErrnoReadOutsideConditionExpressions"); 24681ad6265SDimitry Andric } 24781ad6265SDimitry Andric 24881ad6265SDimitry Andric bool ento::shouldRegisterErrnoChecker(const CheckerManager &mgr) { 24981ad6265SDimitry Andric return true; 25081ad6265SDimitry Andric } 251