diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -348,6 +348,10 @@ let ParentPackage = APIModeling in { +def ErrnoModeling : Checker<"Errno">, + HelpText<"Make the special value 'errno' available to other checkers.">, + Documentation; + def StdCLibraryFunctionsChecker : Checker<"StdCLibraryFunctions">, HelpText<"Improve modeling of the C standard library functions">, // Uninitialized value check is a mandatory dependency. This Checker asserts @@ -1556,6 +1560,11 @@ "purposes.">, Documentation; +def ErrnoTesterChecker : Checker<"ErrnoTest">, + HelpText<"Check modeling aspects of 'errno'.">, + Dependencies<[ErrnoModeling]>, + Documentation; + def ExprInspectionChecker : Checker<"ExprInspection">, HelpText<"Check the analyzer's understanding of expressions">, Documentation; diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -764,7 +764,8 @@ assert(s->getType()->isAnyPointerType() || s->getType()->isReferenceType() || s->getType()->isBlockPointerType()); - assert(isa(sreg) || isa(sreg)); + assert(isa(sreg) || isa(sreg) || + isa(sreg)); } public: @@ -1375,7 +1376,9 @@ const LocationContext *LC); /// Retrieve or create a "symbolic" memory region. - const SymbolicRegion* getSymbolicRegion(SymbolRef Sym); + /// If no memory space is specified, `UnknownSpaceRegion` will be used. + const SymbolicRegion * + getSymbolicRegion(SymbolRef Sym, const MemSpaceRegion *MemSpace = nullptr); /// Return a unique symbolic region belonging to heap memory space. const SymbolicRegion *getSymbolicHeapRegion(SymbolRef sym); diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt --- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -40,6 +40,8 @@ DynamicTypePropagation.cpp DynamicTypeChecker.cpp EnumCastOutOfRangeChecker.cpp + ErrnoModeling.cpp + ErrnoTesterChecker.cpp ExprInspectionChecker.cpp FixedAddressChecker.cpp FuchsiaHandleChecker.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h new file mode 100644 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h @@ -0,0 +1,39 @@ +//=== ErrnoModeling.h - Tracking value of 'errno'. -----------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// Defines inter-checker API for using the system value 'errno'. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ERRNOMODELING_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ERRNOMODELING_H + +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" + +namespace clang { +namespace ento { +namespace errno_modeling { + +/// Returns the value of 'errno', if 'errno' was found in the AST. +llvm::Optional getErrnoValue(ProgramStateRef State); + +/// Set value of 'errno' to any SVal, if possible. +ProgramStateRef setErrnoValue(ProgramStateRef State, + const LocationContext *LCtx, SVal Value); + +/// Set value of 'errno' to a concrete (signed) integer, if possible. +ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C, + uint64_t Value); + +} // namespace errno_modeling +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ERRNOMODELING_H diff --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp @@ -0,0 +1,249 @@ +//=== ErrnoModeling.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 a checker `ErrnoModeling`, which is used to make the system +// value 'errno' available to other checkers. +// The 'errno' value is stored at a special memory region that is accessible +// through the `errno_modeling` namespace. The memory region is either the +// region of `errno` itself if it is a variable, otherwise an artifically +// created region (in the system memory space). If `errno` is defined by using +// a function which returns the address of it (this is always the case if it is +// not a variable) this function is recognized and evaluated. In this way +// `errno` becomes visible to the analysis and checkers can change its value. +// +//===----------------------------------------------------------------------===// + +#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; + +namespace { + +// Name of the "errno" variable. +// FIXME: Is there a system where it is not called "errno" but is a variable? +const char *ErrnoVarName = "errno"; +// Names of functions that return a location of the "errno" value. +// FIXME: Are there other similar function names? +const char *ErrnoLocationFuncNames[] = {"__errno_location", "___errno", + "__errno", "_errno", "__error"}; + +class ErrnoModeling + : public Checker, check::BeginFunction, + check::LiveSymbols, eval::Call> { +public: + void checkASTDecl(const TranslationUnitDecl *D, AnalysisManager &Mgr, + BugReporter &BR) const; + void checkBeginFunction(CheckerContext &C) const; + void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; + bool evalCall(const CallEvent &Call, CheckerContext &C) const; + + // The declaration of an "errno" variable or "errno location" function. + mutable const Decl *ErrnoDecl = nullptr; + +private: + // FIXME: Names from `ErrnoLocationFuncNames` are used to build this set. + CallDescriptionSet ErrnoLocationCalls{{"__errno_location", 0, 0}, + {"___errno", 0, 0}, + {"__errno", 0, 0}, + {"_errno", 0, 0}, + {"__error", 0, 0}}; +}; + +} // namespace + +/// Store a MemRegion that contains the 'errno' integer value. +/// The value is null if the 'errno' value was not recognized in the AST. +REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoRegion, const void *) + +/// An internal function accessing the errno region. +/// Returns null if there isn't any associated memory region. +static const MemRegion *getErrnoRegion(ProgramStateRef State) { + return reinterpret_cast(State->get()); +} + +/// Search for a variable called "errno" in the AST. +/// Return nullptr if not found. +static const VarDecl *getErrnoVar(ASTContext &ACtx) { + IdentifierInfo &II = ACtx.Idents.get(ErrnoVarName); + auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); + auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) { + if (auto *VD = dyn_cast(D)) + return ACtx.getSourceManager().isInSystemHeader(VD->getLocation()) && + VD->hasExternalStorage() && + VD->getType().getCanonicalType() == ACtx.IntTy; + return false; + }); + if (Found == LookupRes.end()) + return nullptr; + + return cast(*Found); +} + +/// Search for a function with a specific name that is used to return a pointer +/// to "errno". +/// Return nullptr if no such function was found. +static const FunctionDecl *getErrnoFunc(ASTContext &ACtx) { + SmallVector LookupRes; + for (StringRef ErrnoName : ErrnoLocationFuncNames) { + IdentifierInfo &II = ACtx.Idents.get(ErrnoName); + llvm::append_range(LookupRes, ACtx.getTranslationUnitDecl()->lookup(&II)); + } + + auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) { + if (auto *FD = dyn_cast(D)) + return ACtx.getSourceManager().isInSystemHeader(FD->getLocation()) && + FD->isExternC() && FD->getNumParams() == 0 && + FD->getReturnType().getCanonicalType() == + ACtx.getPointerType(ACtx.IntTy); + return false; + }); + if (Found == LookupRes.end()) + return nullptr; + + return cast(*Found); +} + +void ErrnoModeling::checkASTDecl(const TranslationUnitDecl *D, + AnalysisManager &Mgr, BugReporter &BR) const { + // Try to find an usable `errno` value. + // It can be an external variable called "errno" or a function that returns a + // pointer to the "errno" value. This function can have different names. + // The actual case is dependent on the C library implementation, we + // can only search for a match in one of these variations. + // We assume that exactly one of these cases might be true. + ErrnoDecl = getErrnoVar(Mgr.getASTContext()); + if (!ErrnoDecl) + ErrnoDecl = getErrnoFunc(Mgr.getASTContext()); +} + +void ErrnoModeling::checkBeginFunction(CheckerContext &C) const { + if (!C.inTopFrame()) + return; + + ASTContext &ACtx = C.getASTContext(); + ProgramStateRef State = C.getState(); + + if (const auto *ErrnoVar = dyn_cast_or_null(ErrnoDecl)) { + // There is an external 'errno' variable. + // Use its memory region. + // The memory region for an 'errno'-like variable is allocated in system + // space by MemRegionManager. + const MemRegion *ErrnoR = + State->getRegion(ErrnoVar, C.getLocationContext()); + assert(ErrnoR && "Memory region should exist for the 'errno' variable."); + State = State->set(ErrnoR); + State = errno_modeling::setErrnoValue(State, C, 0); + C.addTransition(State); + } else if (ErrnoDecl) { + assert(isa(ErrnoDecl) && "Invalid errno location function."); + // There is a function that returns the location of 'errno'. + // We must create a memory region for it in system space. + // Currently a symbolic region is used with an artifical symbol. + // FIXME: It is better to have a custom (new) kind of MemRegion for such + // cases. + SValBuilder &SVB = C.getSValBuilder(); + MemRegionManager &RMgr = C.getStateManager().getRegionManager(); + + const MemSpaceRegion *GlobalSystemSpace = + RMgr.getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind); + + // Create an artifical symbol for the region. + // It is not possible to associate a statement or expression in this case. + const SymbolConjured *Sym = SVB.conjureSymbol( + nullptr, C.getLocationContext(), + ACtx.getLValueReferenceType(ACtx.IntTy), C.blockCount(), &ErrnoDecl); + + // The symbolic region is untyped, create a typed sub-region in it. + // The ElementRegion is used to make the errno region a typed region. + const MemRegion *ErrnoR = RMgr.getElementRegion( + ACtx.IntTy, SVB.makeZeroArrayIndex(), + RMgr.getSymbolicRegion(Sym, GlobalSystemSpace), C.getASTContext()); + State = State->set(ErrnoR); + State = errno_modeling::setErrnoValue(State, C, 0); + C.addTransition(State); + } +} + +bool ErrnoModeling::evalCall(const CallEvent &Call, CheckerContext &C) const { + // Return location of "errno" at a call to an "errno address returning" + // function. + if (ErrnoLocationCalls.contains(Call)) { + ProgramStateRef State = C.getState(); + + const MemRegion *ErrnoR = getErrnoRegion(State); + if (!ErrnoR) + return false; + + State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), + loc::MemRegionVal{ErrnoR}); + C.addTransition(State); + return true; + } + + return false; +} + +void ErrnoModeling::checkLiveSymbols(ProgramStateRef State, + SymbolReaper &SR) const { + // The special errno region should never garbage collected. + if (const auto *ErrnoR = getErrnoRegion(State)) + SR.markLive(ErrnoR); +} + +namespace clang { +namespace ento { +namespace errno_modeling { + +Optional getErrnoValue(ProgramStateRef State) { + const MemRegion *ErrnoR = getErrnoRegion(State); + if (!ErrnoR) + return {}; + QualType IntTy = State->getAnalysisManager().getASTContext().IntTy; + return State->getSVal(ErrnoR, IntTy); +} + +ProgramStateRef setErrnoValue(ProgramStateRef State, + const LocationContext *LCtx, SVal Value) { + const MemRegion *ErrnoR = getErrnoRegion(State); + if (!ErrnoR) + return State; + return State->bindLoc(loc::MemRegionVal{ErrnoR}, Value, LCtx); +} + +ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C, + uint64_t Value) { + const MemRegion *ErrnoR = getErrnoRegion(State); + if (!ErrnoR) + return State; + return State->bindLoc( + loc::MemRegionVal{ErrnoR}, + C.getSValBuilder().makeIntVal(Value, C.getASTContext().IntTy), + C.getLocationContext()); +} + +} // namespace errno_modeling +} // namespace ento +} // namespace clang + +void ento::registerErrnoModeling(CheckerManager &mgr) { + mgr.registerChecker(); +} + +bool ento::shouldRegisterErrnoModeling(const CheckerManager &mgr) { + return true; +} diff --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp @@ -0,0 +1,120 @@ +//=== ErrnoTesterChecker.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 ErrnoTesterChecker, which is used to test functionality of the +// errno_check API. +// +//===----------------------------------------------------------------------===// + +#include "ErrnoModeling.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" + +using namespace clang; +using namespace ento; + +namespace { + +class ErrnoTesterChecker : public Checker { +public: + bool evalCall(const CallEvent &Call, CheckerContext &C) const; + +private: + static void evalSetErrno(CheckerContext &C, const CallEvent &Call); + static void evalGetErrno(CheckerContext &C, const CallEvent &Call); + static void evalSetErrnoIfError(CheckerContext &C, const CallEvent &Call); + static void evalSetErrnoIfErrorRange(CheckerContext &C, + const CallEvent &Call); + + using EvalFn = std::function; + const CallDescriptionMap TestCalls{ + {{"ErrnoTesterChecker_setErrno", 1}, &ErrnoTesterChecker::evalSetErrno}, + {{"ErrnoTesterChecker_getErrno", 0}, &ErrnoTesterChecker::evalGetErrno}, + {{"ErrnoTesterChecker_setErrnoIfError", 0}, + &ErrnoTesterChecker::evalSetErrnoIfError}, + {{"ErrnoTesterChecker_setErrnoIfErrorRange", 0}, + &ErrnoTesterChecker::evalSetErrnoIfErrorRange}}; +}; + +} // namespace + +void ErrnoTesterChecker::evalSetErrno(CheckerContext &C, + const CallEvent &Call) { + C.addTransition(errno_modeling::setErrnoValue( + C.getState(), C.getLocationContext(), Call.getArgSVal(0))); +} + +void ErrnoTesterChecker::evalGetErrno(CheckerContext &C, + const CallEvent &Call) { + ProgramStateRef State = C.getState(); + + Optional ErrnoVal = errno_modeling::getErrnoValue(State); + assert(ErrnoVal && "Errno value should be available."); + State = + State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), *ErrnoVal); + + C.addTransition(State); +} + +void ErrnoTesterChecker::evalSetErrnoIfError(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)); + + ProgramStateRef StateFailure = State->BindExpr( + Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true)); + StateFailure = errno_modeling::setErrnoValue(StateFailure, C, 11); + + C.addTransition(StateSuccess); + C.addTransition(StateFailure); +} + +void ErrnoTesterChecker::evalSetErrnoIfErrorRange(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)); + + ProgramStateRef StateFailure = State->BindExpr( + Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true)); + DefinedOrUnknownSVal ErrnoVal = SVB.conjureSymbolVal( + 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); + + C.addTransition(StateSuccess); + C.addTransition(StateFailure); +} + +bool ErrnoTesterChecker::evalCall(const CallEvent &Call, + CheckerContext &C) const { + const EvalFn *Fn = TestCalls.lookup(Call); + if (Fn) { + (*Fn)(C, Call); + return C.isDifferent(); + } + return false; +} + +void ento::registerErrnoTesterChecker(CheckerManager &Mgr) { + Mgr.registerChecker(); +} + +bool ento::shouldRegisterErrnoTesterChecker(const CheckerManager &Mgr) { + return true; +} diff --git a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp --- a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -1153,9 +1153,12 @@ return getSubRegion(BD, locTy, AC, getCodeRegion()); } -/// getSymbolicRegion - Retrieve or create a "symbolic" memory region. -const SymbolicRegion *MemRegionManager::getSymbolicRegion(SymbolRef sym) { - return getSubRegion(sym, getUnknownRegion()); +const SymbolicRegion * +MemRegionManager::getSymbolicRegion(SymbolRef sym, + const MemSpaceRegion *MemSpace) { + if (MemSpace == nullptr) + MemSpace = getUnknownRegion(); + return getSubRegion(sym, MemSpace); } const SymbolicRegion *MemRegionManager::getSymbolicHeapRegion(SymbolRef Sym) { diff --git a/clang/test/Analysis/Inputs/errno_func.h b/clang/test/Analysis/Inputs/errno_func.h new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/Inputs/errno_func.h @@ -0,0 +1,5 @@ +#pragma clang system_header + +// Define 'errno' as a macro that calls a function. +int *__errno_location(); +#define errno (*__errno_location()) diff --git a/clang/test/Analysis/Inputs/errno_var.h b/clang/test/Analysis/Inputs/errno_var.h new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/Inputs/errno_var.h @@ -0,0 +1,5 @@ +#pragma clang system_header + +// Declare 'errno' as an extern variable in a system header. +// This may be not allowed in C99. +extern int errno; diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h --- a/clang/test/Analysis/Inputs/system-header-simulator.h +++ b/clang/test/Analysis/Inputs/system-header-simulator.h @@ -59,9 +59,6 @@ int ferror(FILE *stream); int fileno(FILE *stream); -// Note, on some platforms errno macro gets replaced with a function call. -extern int errno; - size_t strlen(const char *); char *strcpy(char *restrict, const char *restrict); diff --git a/clang/test/Analysis/analyzer-enabled-checkers.c b/clang/test/Analysis/analyzer-enabled-checkers.c --- a/clang/test/Analysis/analyzer-enabled-checkers.c +++ b/clang/test/Analysis/analyzer-enabled-checkers.c @@ -6,6 +6,7 @@ // CHECK: OVERVIEW: Clang Static Analyzer Enabled Checkers List // CHECK-EMPTY: +// CHECK-NEXT: apiModeling.Errno // CHECK-NEXT: core.CallAndMessageModeling // CHECK-NEXT: apiModeling.StdCLibraryFunctions // CHECK-NEXT: apiModeling.TrustNonnull diff --git a/clang/test/Analysis/errno.c b/clang/test/Analysis/errno.c new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/errno.c @@ -0,0 +1,63 @@ +// RUN: %clang_analyze_cc1 -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=apiModeling.Errno \ +// RUN: -analyzer-checker=debug.ExprInspection \ +// RUN: -analyzer-checker=debug.ErrnoTest \ +// RUN: -DERRNO_VAR + +// RUN: %clang_analyze_cc1 -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=apiModeling.Errno \ +// RUN: -analyzer-checker=debug.ExprInspection \ +// RUN: -analyzer-checker=debug.ErrnoTest \ +// RUN: -DERRNO_FUNC + +#ifdef ERRNO_VAR +#include "Inputs/errno_var.h" +#endif +#ifdef ERRNO_FUNC +#include "Inputs/errno_func.h" +#endif + +void clang_analyzer_eval(int); +void ErrnoTesterChecker_setErrno(int); +int ErrnoTesterChecker_getErrno(); +int ErrnoTesterChecker_setErrnoIfError(); +int ErrnoTesterChecker_setErrnoIfErrorRange(); + +void something(); + +void test() { + // Test if errno is initialized. + clang_analyzer_eval(errno == 0); // expected-warning{{TRUE}} + + ErrnoTesterChecker_setErrno(1); + // Test if errno was recognized and changed. + clang_analyzer_eval(errno == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(ErrnoTesterChecker_getErrno() == 1); // expected-warning{{TRUE}} + + something(); + + // Test if errno was invalidated. + clang_analyzer_eval(errno); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(ErrnoTesterChecker_getErrno()); // expected-warning{{UNKNOWN}} +} + +void testRange(int X) { + if (X > 0) { + ErrnoTesterChecker_setErrno(X); + clang_analyzer_eval(errno > 0); // expected-warning{{TRUE}} + } +} + +void testIfError() { + if (ErrnoTesterChecker_setErrnoIfError()) + clang_analyzer_eval(errno == 11); // expected-warning{{TRUE}} +} + +void testIfErrorRange() { + if (ErrnoTesterChecker_setErrnoIfErrorRange()) { + clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}} + clang_analyzer_eval(errno == 1); // expected-warning{{FALSE}} expected-warning{{TRUE}} + } +} diff --git a/clang/test/Analysis/global-region-invalidation-errno.c b/clang/test/Analysis/global-region-invalidation-errno.c new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/global-region-invalidation-errno.c @@ -0,0 +1,49 @@ +// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -disable-free -verify %s \ +// RUN: -analyzer-checker=core,deadcode,alpha.security.taint \ +// RUN: -DERRNO_VAR + +// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -disable-free -verify %s \ +// RUN: -analyzer-checker=core,deadcode,alpha.security.taint \ +// RUN: -DERRNO_FUNC + +// Note, we do need to include headers here, since the analyzer checks if the function declaration is located in a system header. +// The errno value can be defined in multiple ways, test with each one. +#ifdef ERRNO_VAR +#include "Inputs/errno_var.h" +#endif +#ifdef ERRNO_FUNC +#include "Inputs/errno_func.h" +#endif +#include "Inputs/system-header-simulator.h" + + +void foo(void); + +// expected-no-diagnostics + +// Test errno gets invalidated by a system call. +int testErrnoSystem(void) { + int i; + int *p = 0; + fscanf(stdin, "%d", &i); + if (errno == 0) { + fscanf(stdin, "%d", &i); // errno gets invalidated here. + return 5 / errno; // no-warning + } + + errno = 0; + fscanf(stdin, "%d", &i); // errno gets invalidated here. + return 5 / errno; // no-warning +} + +// Test that errno gets invalidated by internal calls. +int testErrnoInternal(void) { + int i; + int *p = 0; + fscanf(stdin, "%d", &i); + if (errno == 0) { + foo(); // errno gets invalidated here. + return 5 / errno; // no-warning + } + return 0; +} diff --git a/clang/test/Analysis/global-region-invalidation.c b/clang/test/Analysis/global-region-invalidation.c --- a/clang/test/Analysis/global-region-invalidation.c +++ b/clang/test/Analysis/global-region-invalidation.c @@ -1,4 +1,5 @@ -// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -disable-free -analyzer-checker=core,deadcode,alpha.security.taint,debug.TaintTest,debug.ExprInspection -verify %s +// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -disable-free -verify %s \ +// RUN: -analyzer-checker=core,deadcode,alpha.security.taint,debug.TaintTest,debug.ExprInspection void clang_analyzer_eval(int); @@ -35,33 +36,6 @@ return m + j; // expected-warning + {{tainted}} } -// Test errno gets invalidated by a system call. -int testErrnoSystem(void) { - int i; - int *p = 0; - fscanf(stdin, "%d", &i); - if (errno == 0) { - fscanf(stdin, "%d", &i); // errno gets invalidated here. - return 5 / errno; // no-warning - } - - errno = 0; - fscanf(stdin, "%d", &i); // errno gets invalidated here. - return 5 / errno; // no-warning -} - -// Test that errno gets invalidated by internal calls. -int testErrnoInternal(void) { - int i; - int *p = 0; - fscanf(stdin, "%d", &i); - if (errno == 0) { - foo(); // errno gets invalidated here. - return 5 / errno; // no-warning - } - return 0; -} - // Test that const integer does not get invalidated. const int x = 0; int constIntGlob(void) { @@ -124,4 +98,3 @@ static int g = 5; clang_analyzer_eval(g == 5); // expected-warning{{TRUE}} } - diff --git a/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c b/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c --- a/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c +++ b/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c @@ -21,6 +21,7 @@ // CHECK-NEXT: alpha.unix.Stream // CHECK-NEXT: apiModeling.StdCLibraryFunctions // CHECK-NEXT: alpha.unix.StdCLibraryFunctionArgs +// CHECK-NEXT: apiModeling.Errno // CHECK-NEXT: apiModeling.TrustNonnull // CHECK-NEXT: apiModeling.TrustReturnsNonnull // CHECK-NEXT: apiModeling.llvm.CastValue