[clang] 29b512b - [clang][analyzer] Add modeling of 'errno'.
Balázs Kéri via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 25 03:44:14 PST 2022
Author: Balázs Kéri
Date: 2022-02-25T12:42:55+01:00
New Revision: 29b512ba322cb6dd2c45d5e07645e20db47fad0d
URL: https://github.com/llvm/llvm-project/commit/29b512ba322cb6dd2c45d5e07645e20db47fad0d
DIFF: https://github.com/llvm/llvm-project/commit/29b512ba322cb6dd2c45d5e07645e20db47fad0d.diff
LOG: [clang][analyzer] Add modeling of 'errno'.
Add a checker to maintain the system-defined value 'errno'.
The value is supposed to be set in the future by existing or
new checkers that evaluate errno-modifying function calls.
Reviewed By: NoQ, steakhal
Differential Revision: https://reviews.llvm.org/D120310
Added:
clang/lib/StaticAnalyzer/Checkers/Errno.h
clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp
clang/test/Analysis/Inputs/errno_func.h
clang/test/Analysis/Inputs/errno_var.h
clang/test/Analysis/errno.c
Modified:
clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
clang/lib/StaticAnalyzer/Core/MemRegion.cpp
clang/test/Analysis/Inputs/system-header-simulator.h
clang/test/Analysis/analyzer-enabled-checkers.c
clang/test/Analysis/global-region-invalidation.c
clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c
Removed:
################################################################################
diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index fae4240e9d701..6b72d64950106 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -348,6 +348,10 @@ def NullableReturnedFromNonnullChecker : Checker<"NullableReturnedFromNonnull">,
let ParentPackage = APIModeling in {
+def ErrnoModeling : Checker<"Errno">,
+ HelpText<"Make the special value 'errno' available to other checkers.">,
+ Documentation<NotDocumented>;
+
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 @@ def StreamTesterChecker : Checker<"StreamTester">,
"purposes.">,
Documentation<NotDocumented>;
+def ErrnoTesterChecker : Checker<"ErrnoTest">,
+ HelpText<"Check modeling aspects of 'errno'.">,
+ Dependencies<[ErrnoModeling]>,
+ Documentation<NotDocumented>;
+
def ExprInspectionChecker : Checker<"ExprInspection">,
HelpText<"Check the analyzer's understanding of expressions">,
Documentation<NotDocumented>;
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
index 9f85347db5df7..0db6d92c63131 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
@@ -764,7 +764,8 @@ class SymbolicRegion : public SubRegion {
assert(s->getType()->isAnyPointerType() ||
s->getType()->isReferenceType() ||
s->getType()->isBlockPointerType());
- assert(isa<UnknownSpaceRegion>(sreg) || isa<HeapSpaceRegion>(sreg));
+ assert(isa<UnknownSpaceRegion>(sreg) || isa<HeapSpaceRegion>(sreg) ||
+ isa<GlobalSystemSpaceRegion>(sreg));
}
public:
@@ -1375,7 +1376,9 @@ class MemRegionManager {
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
index 44fa2a06c3994..15043c1c632e4 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -40,6 +40,8 @@ add_clang_library(clangStaticAnalyzerCheckers
DynamicTypePropagation.cpp
DynamicTypeChecker.cpp
EnumCastOutOfRangeChecker.cpp
+ ErrnoModeling.cpp
+ ErrnoTesterChecker.cpp
ExprInspectionChecker.cpp
FixedAddressChecker.cpp
FuchsiaHandleChecker.cpp
diff --git a/clang/lib/StaticAnalyzer/Checkers/Errno.h b/clang/lib/StaticAnalyzer/Checkers/Errno.h
new file mode 100644
index 0000000000000..e564809d6bda9
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/Errno.h
@@ -0,0 +1,39 @@
+//=== Errno.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_ERRNO_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ERRNO_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<SVal> 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_ERRNO_H
diff --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
new file mode 100644
index 0000000000000..f61d52e6e9d6e
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
@@ -0,0 +1,253 @@
+//=== 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 "Errno.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::ASTDecl<TranslationUnitDecl>, 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.
+ static const Decl *ErrnoDecl;
+
+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<const MemRegion *>(State->get<ErrnoRegion>());
+}
+
+/// 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<VarDecl>(D))
+ return ACtx.getSourceManager().isInSystemHeader(VD->getLocation()) &&
+ VD->hasExternalStorage() &&
+ VD->getType().getCanonicalType() == ACtx.IntTy;
+ return false;
+ });
+ if (Found == LookupRes.end())
+ return nullptr;
+
+ return cast<VarDecl>(*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<const Decl *> 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<FunctionDecl>(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<FunctionDecl>(*Found);
+}
+
+const Decl *ErrnoModeling::ErrnoDecl = nullptr;
+
+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
diff erent 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.
+ assert(!ErrnoDecl && "Checker should be used for one translation unit.");
+
+ 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<VarDecl>(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<ErrnoRegion>(ErrnoR);
+ State = errno_modeling::setErrnoValue(State, C, 0);
+ C.addTransition(State);
+ } else if (ErrnoDecl) {
+ assert(isa<FunctionDecl>(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<ErrnoRegion>(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<SVal> 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<ErrnoModeling>();
+}
+
+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
index 0000000000000..62d536e54ec37
--- /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 "Errno.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<eval::Call> {
+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<void(CheckerContext &, const CallEvent &)>;
+ const CallDescriptionMap<EvalFn> 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<SVal> 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<ErrnoTesterChecker>();
+}
+
+bool ento::shouldRegisterErrnoTesterChecker(const CheckerManager &Mgr) {
+ return true;
+}
diff --git a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
index 58bea12f87836..6d0b5f85d84ed 100644
--- a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -1153,9 +1153,12 @@ MemRegionManager::getBlockCodeRegion(const BlockDecl *BD, CanQualType locTy,
return getSubRegion<BlockCodeRegion>(BD, locTy, AC, getCodeRegion());
}
-/// getSymbolicRegion - Retrieve or create a "symbolic" memory region.
-const SymbolicRegion *MemRegionManager::getSymbolicRegion(SymbolRef sym) {
- return getSubRegion<SymbolicRegion>(sym, getUnknownRegion());
+const SymbolicRegion *
+MemRegionManager::getSymbolicRegion(SymbolRef sym,
+ const MemSpaceRegion *MemSpace) {
+ if (MemSpace == nullptr)
+ MemSpace = getUnknownRegion();
+ return getSubRegion<SymbolicRegion>(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
index 0000000000000..a6c74b1a08d1e
--- /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
index 0000000000000..82b1e9bf6285d
--- /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
index b72f45a9b0e55..70345c5f4022d 100644
--- a/clang/test/Analysis/Inputs/system-header-simulator.h
+++ b/clang/test/Analysis/Inputs/system-header-simulator.h
@@ -59,9 +59,6 @@ int feof(FILE *stream);
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
index 7437e79a498f1..fa306d78d5553 100644
--- 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
index 0000000000000..c2697d8891b19
--- /dev/null
+++ b/clang/test/Analysis/errno.c
@@ -0,0 +1,58 @@
+// 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_HEADER=\"Inputs/errno_var.h\"
+
+// 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_HEADER=\"Inputs/errno_func.h\"
+
+#include ERRNO_HEADER
+
+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.c b/clang/test/Analysis/global-region-invalidation.c
index 74ff72cfae00d..123f51fe633d7 100644
--- a/clang/test/Analysis/global-region-invalidation.c
+++ b/clang/test/Analysis/global-region-invalidation.c
@@ -1,8 +1,15 @@
-// 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 \
+// RUN: -DERRNO_HEADER=\"Inputs/errno_var.h\" %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 \
+// RUN: -DERRNO_HEADER=\"Inputs/errno_func.h\"
void clang_analyzer_eval(int);
// Note, we do need to include headers here, since the analyzer checks if the function declaration is located in a system header.
+#include ERRNO_HEADER
#include "Inputs/system-header-simulator.h"
// Test that system header does not invalidate the internal global.
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
index a64ec4a71487a..f00e10a0f056f 100644
--- 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
More information about the cfe-commits
mailing list