balazske created this revision. Herald added subscribers: steakhal, manas, ASDenysPetrov, martong, gamesh411, dkrupp, donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, xazax.hun, mgorny. Herald added a reviewer: Szelethus. Herald added a project: All. balazske requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
Extend checker 'ErrnoModeling' with a state of 'errno' to indicate the importance of the 'errno' value and how it should be used. Add a new checker 'ErrnoChecker' that observes use of 'errno' and finds possible wrong uses, based on the "errno state". The "errno state" should be set (together with value of 'errno') by other checkers (that perform modeling of the given function) in the future. Currently only a test function can set this value. The new checker has no user-observable effect yet. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D122150 Files: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp clang/test/Analysis/errno.c
Index: clang/test/Analysis/errno.c =================================================================== --- clang/test/Analysis/errno.c +++ clang/test/Analysis/errno.c @@ -3,6 +3,7 @@ // RUN: -analyzer-checker=apiModeling.Errno \ // RUN: -analyzer-checker=debug.ExprInspection \ // RUN: -analyzer-checker=debug.ErrnoTest \ +// RUN: -analyzer-checker=alpha.unix.Errno \ // RUN: -DERRNO_VAR // RUN: %clang_analyze_cc1 -verify %s \ @@ -10,8 +11,10 @@ // RUN: -analyzer-checker=apiModeling.Errno \ // RUN: -analyzer-checker=debug.ExprInspection \ // RUN: -analyzer-checker=debug.ErrnoTest \ +// RUN: -analyzer-checker=alpha.unix.Errno \ // RUN: -DERRNO_FUNC +#include "Inputs/system-header-simulator.h" #ifdef ERRNO_VAR #include "Inputs/errno_var.h" #endif @@ -24,6 +27,7 @@ int ErrnoTesterChecker_getErrno(); int ErrnoTesterChecker_setErrnoIfError(); int ErrnoTesterChecker_setErrnoIfErrorRange(); +int ErrnoTesterChecker_setErrnoCheckState(); void something(); @@ -61,3 +65,109 @@ clang_analyzer_eval(errno == 1); // expected-warning{{FALSE}} expected-warning{{TRUE}} } } + +void testErrnoCheck0() { + // If the function returns a success result code, value of 'errno' + // is unspecified and it is unsafe to make any decision with it. + // The function did not promise to not change 'errno' if no failure happens. + int X = ErrnoTesterChecker_setErrnoCheckState(); + if (X == 0) { + if (errno) { // expected-warning{{Value of 'errno' could be undefined after a call to a function that does not promise to not change 'errno' [alpha.unix.Errno]}} + } + if (errno) { + } + } + X = ErrnoTesterChecker_setErrnoCheckState(); + if (X == 0) { + if (errno) { // expected-warning{{Value of 'errno' could be undefined after a call to a function that does not promise to not change 'errno' [alpha.unix.Errno]}} + } + errno = 0; + } + X = ErrnoTesterChecker_setErrnoCheckState(); + if (X == 0) { + errno = 0; + if (errno) { + } + } +} + +void testErrnoCheck1() { + // If the function returns error result code that is out-of-band (not a valid + // non-error return value) the value of 'errno' can be checked but it is not + // required to do so. + // The same applies if the function has an in-band error result code but not + // the error result code was returned. + int X = ErrnoTesterChecker_setErrnoCheckState(); + if (X == 1) { + if (errno) { + } + } + X = ErrnoTesterChecker_setErrnoCheckState(); + if (X == 1) { + errno = 0; + } +} + +void testErrnoCheck2() { + // If the function returns an in-band error result the value of 'errno' is + // required to be checked to verify if error happened or not. + // The same applies to other functions that can indicate failure only by + // change of 'errno'. + int X = ErrnoTesterChecker_setErrnoCheckState(); + if (X == 2) { + if (errno) { + } + errno = 0; + } + X = ErrnoTesterChecker_setErrnoCheckState(); + if (X == 2) { + errno = 0; // expected-warning{{Value of 'errno' was not checked after a call to a function that possibly failed and indicates failure only by value of 'errno' [alpha.unix.Errno]}} + errno = 0; + } + X = ErrnoTesterChecker_setErrnoCheckState(); + if (X == 2) { + errno = 0; // expected-warning{{Value of 'errno' was not checked after a call to a function that possibly failed and indicates failure only by value of 'errno' [alpha.unix.Errno]}} + if (errno) { + } + } +} + +void testErrnoCheckUndefinedLoad() { + int X = ErrnoTesterChecker_setErrnoCheckState(); + if (X == 0) { + int Y = errno; // expected-warning{{Value of 'errno' could be undefined after a call to a function that does not promise to not change 'errno' [alpha.unix.Errno]}} + } +} + +void testErrnoNotCheckedAtSystemCall() { + int X = ErrnoTesterChecker_setErrnoCheckState(); + if (X == 2) { + printf("%i", 1); // expected-warning{{Value of 'errno' was not checked after a call to a function that possibly failed and indicates failure only by value of 'errno' [alpha.unix.Errno]}} + // expected-note@-1{{Function 'printf' might overwrite value of 'errno'}} + printf("%i", 1); + } +} + +void testErrnoCheckStateInvalidate() { + int X = ErrnoTesterChecker_setErrnoCheckState(); + if (X == 0) { + something(); + if (errno) { + } + } + X = ErrnoTesterChecker_setErrnoCheckState(); + if (X == 0) { + printf("%i", 1); + if (errno) { + } + } +} + +void testErrnoCheckStateInvalidate1() { + int X = ErrnoTesterChecker_setErrnoCheckState(); + if (X == 2) { + clang_analyzer_eval(errno); // expected-warning{{TRUE}} + something(); + clang_analyzer_eval(errno); // expected-warning{{UNKNOWN}} + } +} Index: clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp @@ -20,6 +20,7 @@ using namespace clang; using namespace ento; +using namespace errno_modeling; namespace { @@ -33,6 +34,7 @@ static void evalSetErrnoIfError(CheckerContext &C, const CallEvent &Call); static void evalSetErrnoIfErrorRange(CheckerContext &C, const CallEvent &Call); + static void evalSetErrnoCheckState(CheckerContext &C, const CallEvent &Call); using EvalFn = std::function<void(CheckerContext &, const CallEvent &)>; const CallDescriptionMap<EvalFn> TestCalls{ @@ -41,22 +43,24 @@ {{"ErrnoTesterChecker_setErrnoIfError", 0}, &ErrnoTesterChecker::evalSetErrnoIfError}, {{"ErrnoTesterChecker_setErrnoIfErrorRange", 0}, - &ErrnoTesterChecker::evalSetErrnoIfErrorRange}}; + &ErrnoTesterChecker::evalSetErrnoIfErrorRange}, + {{"ErrnoTesterChecker_setErrnoCheckState", 0}, + &ErrnoTesterChecker::evalSetErrnoCheckState}}; }; } // namespace void ErrnoTesterChecker::evalSetErrno(CheckerContext &C, const CallEvent &Call) { - C.addTransition(errno_modeling::setErrnoValue( - C.getState(), C.getLocationContext(), Call.getArgSVal(0))); + C.addTransition(setErrnoValue(C.getState(), C.getLocationContext(), + Call.getArgSVal(0), Errno_Irrelevant)); } void ErrnoTesterChecker::evalGetErrno(CheckerContext &C, const CallEvent &Call) { ProgramStateRef State = C.getState(); - Optional<SVal> ErrnoVal = errno_modeling::getErrnoValue(State); + Optional<SVal> ErrnoVal = getErrnoValue(State); assert(ErrnoVal && "Errno value should be available."); State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), *ErrnoVal); @@ -74,7 +78,7 @@ ProgramStateRef StateFailure = State->BindExpr( Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true)); - StateFailure = errno_modeling::setErrnoValue(StateFailure, C, 11); + StateFailure = setErrnoValue(StateFailure, C, 11, Errno_Irrelevant); C.addTransition(StateSuccess); C.addTransition(StateFailure); @@ -94,13 +98,35 @@ nullptr, Call.getOriginExpr(), C.getLocationContext(), C.blockCount()); StateFailure = StateFailure->assume(ErrnoVal, true); assert(StateFailure && "Failed to assume on an initial value."); - StateFailure = errno_modeling::setErrnoValue( - StateFailure, C.getLocationContext(), ErrnoVal); + StateFailure = setErrnoValue(StateFailure, C.getLocationContext(), ErrnoVal, + Errno_Irrelevant); C.addTransition(StateSuccess); C.addTransition(StateFailure); } +void ErrnoTesterChecker::evalSetErrnoCheckState(CheckerContext &C, + const CallEvent &Call) { + ProgramStateRef State = C.getState(); + SValBuilder &SVB = C.getSValBuilder(); + + ProgramStateRef StateSuccess = State->BindExpr( + Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true)); + StateSuccess = setErrnoValue(StateSuccess, C, 0, Errno_MustNotBeChecked); + + ProgramStateRef StateFailure1 = State->BindExpr( + Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true)); + StateFailure1 = setErrnoValue(StateFailure1, C, 1, Errno_Irrelevant); + + ProgramStateRef StateFailure2 = State->BindExpr( + Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(2, true)); + StateFailure2 = setErrnoValue(StateFailure2, C, 2, Errno_MustBeChecked); + + C.addTransition(StateSuccess); + C.addTransition(StateFailure1); + C.addTransition(StateFailure2); +} + bool ErrnoTesterChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { const EvalFn *Fn = TestCalls.lookup(Call); Index: clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h =================================================================== --- clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h +++ clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h @@ -21,16 +21,31 @@ namespace ento { namespace errno_modeling { +enum ErrnoCheckState : unsigned { + Errno_Irrelevant = 0, + Errno_MustBeChecked = 1, + Errno_MustNotBeChecked = 2 +}; + /// Returns the value of 'errno', if 'errno' was found in the AST. llvm::Optional<SVal> getErrnoValue(ProgramStateRef State); +ErrnoCheckState getErrnoState(ProgramStateRef State); + +llvm::Optional<Loc> getErrnoLoc(ProgramStateRef State); + /// Set value of 'errno' to any SVal, if possible. ProgramStateRef setErrnoValue(ProgramStateRef State, - const LocationContext *LCtx, SVal Value); + const LocationContext *LCtx, SVal Value, + ErrnoCheckState EState); /// Set value of 'errno' to a concrete (signed) integer, if possible. ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C, - uint64_t Value); + uint64_t Value, ErrnoCheckState EState); + +ProgramStateRef setErrnoStateIrrelevant(ProgramStateRef State); + +bool isErrno(const Decl *D); } // namespace errno_modeling } // namespace ento Index: clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp +++ clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp @@ -76,6 +76,8 @@ return reinterpret_cast<const MemRegion *>(State->get<ErrnoRegion>()); } +REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoState, unsigned) + /// Search for a variable called "errno" in the AST. /// Return nullptr if not found. static const VarDecl *getErrnoVar(ASTContext &ACtx) { @@ -147,7 +149,8 @@ State->getRegion(ErrnoVar, C.getLocationContext()); assert(ErrnoR && "Memory region should exist for the 'errno' variable."); State = State->set<ErrnoRegion>(ErrnoR); - State = errno_modeling::setErrnoValue(State, C, 0); + State = errno_modeling::setErrnoValue(State, C, 0, + errno_modeling::Errno_Irrelevant); C.addTransition(State); } else if (ErrnoDecl) { assert(isa<FunctionDecl>(ErrnoDecl) && "Invalid errno location function."); @@ -174,7 +177,8 @@ ACtx.IntTy, SVB.makeZeroArrayIndex(), RMgr.getSymbolicRegion(Sym, GlobalSystemSpace), C.getASTContext()); State = State->set<ErrnoRegion>(ErrnoR); - State = errno_modeling::setErrnoValue(State, C, 0); + State = errno_modeling::setErrnoValue(State, C, 0, + errno_modeling::Errno_Irrelevant); C.addTransition(State); } } @@ -218,22 +222,55 @@ } ProgramStateRef setErrnoValue(ProgramStateRef State, - const LocationContext *LCtx, SVal Value) { + const LocationContext *LCtx, SVal Value, + ErrnoCheckState EState) { const MemRegion *ErrnoR = getErrnoRegion(State); if (!ErrnoR) return State; - return State->bindLoc(loc::MemRegionVal{ErrnoR}, Value, LCtx); + // First set the errno value, the old state is still available at 'checkBind' + // or 'checkLocation' for errno value. + State = State->bindLoc(loc::MemRegionVal{ErrnoR}, Value, LCtx); + return State->set<ErrnoState>(EState); } ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C, - uint64_t Value) { + uint64_t Value, ErrnoCheckState EState) { const MemRegion *ErrnoR = getErrnoRegion(State); if (!ErrnoR) return State; - return State->bindLoc( + State = State->bindLoc( loc::MemRegionVal{ErrnoR}, C.getSValBuilder().makeIntVal(Value, C.getASTContext().IntTy), C.getLocationContext()); + return State->set<ErrnoState>(EState); +} + +Optional<Loc> getErrnoLoc(ProgramStateRef State) { + const MemRegion *ErrnoR = getErrnoRegion(State); + if (!ErrnoR) + return {}; + return loc::MemRegionVal{ErrnoR}; +} + +ProgramStateRef setErrnoStateIrrelevant(ProgramStateRef State) { + return State->set<ErrnoState>(Errno_Irrelevant); +} + +ErrnoCheckState getErrnoState(ProgramStateRef State) { + if (unsigned EState = State->get<ErrnoState>()) + return static_cast<ErrnoCheckState>(EState); + return Errno_Irrelevant; +} + +bool isErrno(const Decl *D) { + if (const auto *VD = dyn_cast_or_null<VarDecl>(D)) + return VD->getIdentifier() && + VD->getIdentifier()->getName() == ErrnoVarName; + if (const auto *FD = dyn_cast_or_null<FunctionDecl>(D)) + if (IdentifierInfo *II = FD->getIdentifier()) + return llvm::find(ErrnoLocationFuncNames, II->getName()) != + std::end(ErrnoLocationFuncNames); + return false; } } // namespace errno_modeling Index: clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp =================================================================== --- /dev/null +++ clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp @@ -0,0 +1,218 @@ +//=== ErrnoChecker.cpp ------------------------------------------*- 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 an "errno checker" that can detect some invalid use of the +// system-defined value 'errno'. This checker works together with the +// ErrnoModeling checker. +// +//===----------------------------------------------------------------------===// + +#include "ErrnoModeling.h" +#include "clang/AST/ParentMapContext.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "llvm/ADT/STLExtras.h" + +using namespace clang; +using namespace ento; +using namespace errno_modeling; + +namespace { + +class ErrnoChecker : public Checker<check::BeginFunction, check::Location, + check::PreCall, check::RegionChanges> { +public: + void checkBeginFunction(CheckerContext &C) const; + void checkLocation(SVal Loc, bool IsLoad, const Stmt *S, + CheckerContext &) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + ProgramStateRef + checkRegionChanges(ProgramStateRef State, + const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const LocationContext *LCtx, const CallEvent *Call) const; + +private: + mutable bool ErrnoInitialized = false; + mutable Optional<Loc> ErrnoLoc; + mutable const MemRegion *ErrnoRegion = nullptr; + + void initErrno(CheckerContext &C) const; + void generateErrnoNotCheckedBug(CheckerContext &C, ProgramStateRef State, + const CallEvent *CallMayChangeErrno) const; + + BugType BT_InvalidErrnoRead{this, "Value of 'errno' could be undefined", + "Error handling"}; + BugType BT_ErrnoNotChecked{this, "Value of 'errno' was not checked", + "Error handling"}; +}; + +} // namespace + +void ErrnoChecker::initErrno(CheckerContext &C) const { + if (ErrnoInitialized) + return; + + ErrnoLoc = getErrnoLoc(C.getState()); + if (ErrnoLoc) { + ErrnoRegion = ErrnoLoc->getAsRegion(); + assert(ErrnoRegion && "The 'errno' location should be a memory region."); + } + ErrnoInitialized = true; +} + +void ErrnoChecker::generateErrnoNotCheckedBug( + CheckerContext &C, ProgramStateRef State, + const CallEvent *CallMayChangeErrno) const { + if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { + auto BR = std::make_unique<PathSensitiveBugReport>( + BT_ErrnoNotChecked, + "Value of 'errno' was not checked after a call to a function " + "that possibly failed and indicates failure only by value of " + "'errno'", + N); + if (CallMayChangeErrno) { + SmallString<100> StrBuf; + llvm::raw_svector_ostream OS(StrBuf); + OS << "Function '"; + const auto *CallD = + dyn_cast_or_null<FunctionDecl>(CallMayChangeErrno->getDecl()); + assert(CallD && CallD->getIdentifier()); + OS << CallD->getIdentifier()->getName(); + OS << "' might overwrite value of 'errno'"; + PathDiagnosticLocation Loc{ + CallMayChangeErrno->getSourceRange().getBegin(), + C.getSourceManager()}; + BR->addNote(OS.str(), Loc, {CallMayChangeErrno->getSourceRange()}); + } + C.emitReport(std::move(BR)); + } +} + +void ErrnoChecker::checkBeginFunction(CheckerContext &C) const { + // The errno location must be refreshed at every new function. + ErrnoInitialized = false; +} + +void ErrnoChecker::checkLocation(SVal Loc, bool IsLoad, const Stmt *S, + CheckerContext &C) const { + initErrno(C); + if (!ErrnoLoc) + return; + + if (auto L = Loc.getAs<ento::Loc>()) { + if (*ErrnoLoc != *L) + return; + + ProgramStateRef State = C.getState(); + ErrnoCheckState EState = getErrnoState(State); + + if (IsLoad) { + switch (EState) { + case Errno_MustNotBeChecked: + // Read of 'errno' when it may have undefined value. + if (ExplodedNode *N = C.generateErrorNode()) { + C.emitReport(std::make_unique<PathSensitiveBugReport>( + BT_InvalidErrnoRead, + "Value of 'errno' could be undefined after a call to a function " + "that does not promise to not change 'errno'.", + N)); + } + break; + case Errno_MustBeChecked: + // 'errno' has to be checked. A load is required for this, with no more + // information we can assume that it is checked somehow. + // After this place 'errno' is allowed to be read and written. + State = setErrnoStateIrrelevant(State); + C.addTransition(State); + break; + default: + break; + } + } else { + switch (EState) { + case Errno_MustBeChecked: + // 'errno' is overwritten without a read before but it should have been + // checked. + generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(State), nullptr); + break; + case Errno_MustNotBeChecked: + // Write to 'errno' when it is not allowed to be read. + // After this place 'errno' is allowed to be read and written. + State = setErrnoStateIrrelevant(State); + C.addTransition(State); + break; + default: + break; + } + } + } +} + +void ErrnoChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + const auto *CallF = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); + if (!CallF) + return; + + initErrno(C); + if (!ErrnoLoc) + return; + + CallF = CallF->getCanonicalDecl(); + // If 'errno' must be checked, it should be done as soon as possible, and + // before any other call to a system function (something in a system header). + // To avoid use of a long list of functions that may change 'errno' + // (which may be different with standard library versions) assume that any + // function can change it. + // A list of special functions can be used that are allowed here without + // generation of diagnostic. For now the only such case is 'errno' itself. + // Probably 'strerror'? + if (CallF->isExternC() && CallF->isGlobal() && + C.getSourceManager().isInSystemHeader(CallF->getLocation()) && + !isErrno(CallF)) { + if (getErrnoState(C.getState()) == Errno_MustBeChecked) + generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(C.getState()), + &Call); + } +} + +ProgramStateRef ErrnoChecker::checkRegionChanges( + ProgramStateRef State, const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx, + const CallEvent *Call) const { + // If 'errno' is invalidated we can not know if it is checked or written into, + // allow read and write without bug reports. + if (llvm::find(Regions, ErrnoRegion) != Regions.end()) + return setErrnoStateIrrelevant(State); + + // Always reset errno state when the system memory space is invalidated. + // The ErrnoRegion is not always found in the list in this case. + const MemSpaceRegion *GlobalSystemSpace = + State->getStateManager().getRegionManager().getGlobalsRegion( + MemRegion::GlobalSystemSpaceRegionKind); + if (llvm::find(Regions, GlobalSystemSpace) != Regions.end()) + return setErrnoStateIrrelevant(State); + + return State; +} + +void ento::registerErrnoChecker(CheckerManager &mgr) { + mgr.registerChecker<ErrnoChecker>(); +} + +bool ento::shouldRegisterErrnoChecker(const CheckerManager &mgr) { + return true; +} Index: clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt =================================================================== --- clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -40,6 +40,7 @@ DynamicTypePropagation.cpp DynamicTypeChecker.cpp EnumCastOutOfRangeChecker.cpp + ErrnoChecker.cpp ErrnoModeling.cpp ErrnoTesterChecker.cpp ExprInspectionChecker.cpp Index: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -539,6 +539,11 @@ let ParentPackage = UnixAlpha in { +def ErrnoChecker : Checker<"Errno">, + HelpText<"Check not recommended uses of 'errno'">, + Dependencies<[ErrnoModeling]>, + Documentation<HasAlphaDocumentation>; + def ChrootChecker : Checker<"Chroot">, HelpText<"Check improper use of chroot">, Documentation<HasAlphaDocumentation>;
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits