[clang] 60f3b07 - [clang][analyzer] Add checker for bad use of 'errno'.

Balázs Kéri via cfe-commits cfe-commits at lists.llvm.org
Mon Jun 20 01:08:28 PDT 2022


Author: Balázs Kéri
Date: 2022-06-20T10:07:31+02:00
New Revision: 60f3b071185bf4be32d5c3376856c573975c912a

URL: https://github.com/llvm/llvm-project/commit/60f3b071185bf4be32d5c3376856c573975c912a
DIFF: https://github.com/llvm/llvm-project/commit/60f3b071185bf4be32d5c3376856c573975c912a.diff

LOG: [clang][analyzer] Add checker for bad use of 'errno'.

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.

Reviewed By: martong, steakhal

Differential Revision: https://reviews.llvm.org/D122150

Added: 
    clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp
    clang/test/Analysis/errno-notes.c
    clang/test/Analysis/errno-options.c

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/docs/analyzer/checkers.rst
    clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
    clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
    clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
    clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h
    clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp
    clang/test/Analysis/analyzer-config.c
    clang/test/Analysis/errno.c

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index e37b93e573491..6b4616d43bc29 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -581,6 +581,10 @@ Static Analyzer
   `strcmp``, ``strncmp``, ``strcpy``, ``strlen``, ``strsep`` and many more. Although
   this checker currently is in list of alpha checkers due to a false positive.
 
+- Added a new checker ``alpha.unix.Errno``. This can find the first read
+  of ``errno`` after successful standard function calls, such use of ``errno``
+  could be unsafe.
+
 - Deprecate the ``-analyzer-store region`` and
   ``-analyzer-opt-analyze-nested-blocks`` analyzer flags.
   These flags are still accepted, but a warning will be displayed.

diff  --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst
index 6a73e66b0daaa..6f2486fb49dfc 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -2520,6 +2520,75 @@ Check improper use of chroot.
    f(); // warn: no call of chdir("/") immediately after chroot
  }
 
+.. _alpha-unix-Errno:
+
+alpha.unix.Errno (C)
+""""""""""""""""""""
+
+Check for improper use of ``errno``.
+This checker implements partially CERT rule
+`ERR30-C. Set errno to zero before calling a library function known to set errno,
+and check errno only after the function returns a value indicating failure
+<https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152351>`_.
+The checker can find the first read of ``errno`` after successful standard
+function calls.
+
+The C and POSIX standards often do not define if a standard library function
+may change value of ``errno`` if the call does not fail.
+Therefore, ``errno`` should only be used if it is known from the return value
+of a function that the call has failed.
+There are exceptions to this rule (for example ``strtol``) but the affected
+functions are not yet supported by the checker.
+The return values for the failure cases are documented in the standard Linux man
+pages of the functions and in the `POSIX standard <https://pubs.opengroup.org/onlinepubs/9699919799/>`_.
+
+.. code-block:: c
+
+ int unsafe_errno_read(int sock, void *data, int data_size) {
+   if (send(sock, data, data_size, 0) != data_size) {
+     // 'send' can be successful even if not all data was sent
+     if (errno == 1) { // An undefined value may be read from 'errno'
+       return 0;
+     }
+   }
+   return 1;
+ }
+
+The supported functions are the same that are modeled by checker
+:ref:`alpha-unix-StdCLibraryFunctionArgs`.
+The ``ModelPOSIX`` option of that checker affects the set of checked functions.
+
+**Parameters**
+
+The ``AllowErrnoReadOutsideConditionExpressions`` option allows read of the
+errno value if the value is not used in a condition (in ``if`` statements,
+loops, conditional expressions, ``switch`` statements). For example ``errno``
+can be stored into a variable without getting a warning by the checker.
+
+.. code-block:: c
+
+ int unsafe_errno_read(int sock, void *data, int data_size) {
+   if (send(sock, data, data_size, 0) != data_size) {
+     int err = errno;
+     // warning if 'AllowErrnoReadOutsideConditionExpressions' is false
+     // no warning if 'AllowErrnoReadOutsideConditionExpressions' is true
+   }
+   return 1;
+ }
+
+Default value of this option is ``true``. This allows save of the errno value
+for possible later error handling.
+
+**Limitations**
+
+ - Only the very first usage of ``errno`` is checked after an affected function
+   call. Value of ``errno`` is not followed when it is stored into a variable
+   or returned from a function.
+ - Documentation of function ``lseek`` is not clear about what happens if the
+   function returns 
diff erent value than the expected file position but not -1.
+   To avoid possible false-positives ``errno`` is allowed to be used in this
+   case.
+
 .. _alpha-unix-PthreadLock:
 
 alpha.unix.PthreadLock (C)

diff  --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 1bcbab4985156..cf8cec3b13c3a 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -546,6 +546,18 @@ def VforkChecker : Checker<"Vfork">,
 
 let ParentPackage = UnixAlpha in {
 
+def ErrnoChecker : Checker<"Errno">,
+  HelpText<"Check for improper use of 'errno'">,
+  Dependencies<[ErrnoModeling]>,
+  CheckerOptions<[
+    CmdLineOption<Boolean,
+                  "AllowErrnoReadOutsideConditionExpressions",
+                  "Allow read of undefined value from errno outside of conditions",
+                  "true",
+                  InAlpha>,
+  ]>,
+  Documentation<HasDocumentation>;
+
 def ChrootChecker : Checker<"Chroot">,
   HelpText<"Check improper use of chroot">,
   Documentation<HasDocumentation>;

diff  --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
index 15043c1c632e4..84886b93d2e4a 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -40,6 +40,7 @@ add_clang_library(clangStaticAnalyzerCheckers
   DynamicTypePropagation.cpp
   DynamicTypeChecker.cpp
   EnumCastOutOfRangeChecker.cpp
+  ErrnoChecker.cpp
   ErrnoModeling.cpp
   ErrnoTesterChecker.cpp
   ExprInspectionChecker.cpp

diff  --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp
new file mode 100644
index 0000000000000..d8c3d7a4a6a66
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp
@@ -0,0 +1,249 @@
+//=== 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 and other checkers like StdCLibraryFunctions.
+//
+//===----------------------------------------------------------------------===//
+
+#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::Location, check::PreCall, check::RegionChanges> {
+public:
+  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;
+  void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const;
+
+  /// Indicates if a read (load) of \c errno is allowed in a non-condition part
+  /// of \c if, \c switch, loop and conditional statements when the errno
+  /// value may be undefined.
+  bool AllowErrnoReadOutsideConditions = true;
+
+private:
+  void generateErrnoNotCheckedBug(CheckerContext &C, ProgramStateRef State,
+                                  const MemRegion *ErrnoRegion,
+                                  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
+
+static ProgramStateRef setErrnoStateIrrelevant(ProgramStateRef State) {
+  return setErrnoState(State, Irrelevant);
+}
+
+/// Check if a statement (expression) or an ancestor of it is in a condition
+/// part of a (conditional, loop, switch) statement.
+static bool isInCondition(const Stmt *S, CheckerContext &C) {
+  ParentMapContext &ParentCtx = C.getASTContext().getParentMapContext();
+  bool CondFound = false;
+  while (S && !CondFound) {
+    const DynTypedNodeList Parents = ParentCtx.getParents(*S);
+    if (Parents.empty())
+      break;
+    const auto *ParentS = Parents[0].get<Stmt>();
+    if (!ParentS || isa<CallExpr>(ParentS))
+      break;
+    switch (ParentS->getStmtClass()) {
+    case Expr::IfStmtClass:
+      CondFound = (S == cast<IfStmt>(ParentS)->getCond());
+      break;
+    case Expr::ForStmtClass:
+      CondFound = (S == cast<ForStmt>(ParentS)->getCond());
+      break;
+    case Expr::DoStmtClass:
+      CondFound = (S == cast<DoStmt>(ParentS)->getCond());
+      break;
+    case Expr::WhileStmtClass:
+      CondFound = (S == cast<WhileStmt>(ParentS)->getCond());
+      break;
+    case Expr::SwitchStmtClass:
+      CondFound = (S == cast<SwitchStmt>(ParentS)->getCond());
+      break;
+    case Expr::ConditionalOperatorClass:
+      CondFound = (S == cast<ConditionalOperator>(ParentS)->getCond());
+      break;
+    case Expr::BinaryConditionalOperatorClass:
+      CondFound = (S == cast<BinaryConditionalOperator>(ParentS)->getCommon());
+      break;
+    default:
+      break;
+    }
+    S = ParentS;
+  }
+  return CondFound;
+}
+
+void ErrnoChecker::generateErrnoNotCheckedBug(
+    CheckerContext &C, ProgramStateRef State, const MemRegion *ErrnoRegion,
+    const CallEvent *CallMayChangeErrno) const {
+  if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
+    SmallString<100> StrBuf;
+    llvm::raw_svector_ostream OS(StrBuf);
+    if (CallMayChangeErrno) {
+      OS << "Value of 'errno' was not checked and may be overwritten by "
+            "function '";
+      const auto *CallD =
+          dyn_cast_or_null<FunctionDecl>(CallMayChangeErrno->getDecl());
+      assert(CallD && CallD->getIdentifier());
+      OS << CallD->getIdentifier()->getName() << "'";
+    } else {
+      OS << "Value of 'errno' was not checked and is overwritten here";
+    }
+    auto BR = std::make_unique<PathSensitiveBugReport>(BT_ErrnoNotChecked,
+                                                       OS.str(), N);
+    BR->markInteresting(ErrnoRegion);
+    C.emitReport(std::move(BR));
+  }
+}
+
+void ErrnoChecker::checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
+                                 CheckerContext &C) const {
+  Optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState());
+  if (!ErrnoLoc)
+    return;
+
+  auto L = Loc.getAs<ento::Loc>();
+  if (!L || *ErrnoLoc != *L)
+    return;
+
+  ProgramStateRef State = C.getState();
+  ErrnoCheckState EState = getErrnoState(State);
+
+  if (IsLoad) {
+    switch (EState) {
+    case MustNotBeChecked:
+      // Read of 'errno' when it may have undefined value.
+      if (!AllowErrnoReadOutsideConditions || isInCondition(S, C)) {
+        if (ExplodedNode *N = C.generateErrorNode()) {
+          auto BR = std::make_unique<PathSensitiveBugReport>(
+              BT_InvalidErrnoRead,
+              "An undefined value may be read from 'errno'", N);
+          BR->markInteresting(ErrnoLoc->getAsRegion());
+          C.emitReport(std::move(BR));
+        }
+      }
+      break;
+    case 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 MustBeChecked:
+      // 'errno' is overwritten without a read before but it should have been
+      // checked.
+      generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(State),
+                                 ErrnoLoc->getAsRegion(), nullptr);
+      break;
+    case 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;
+
+  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 
diff erent 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()) == MustBeChecked) {
+      Optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState());
+      assert(ErrnoLoc && "ErrnoLoc should exist if an errno state is set.");
+      generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(C.getState()),
+                                 ErrnoLoc->getAsRegion(), &Call);
+    }
+  }
+}
+
+ProgramStateRef ErrnoChecker::checkRegionChanges(
+    ProgramStateRef State, const InvalidatedSymbols *Invalidated,
+    ArrayRef<const MemRegion *> ExplicitRegions,
+    ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
+    const CallEvent *Call) const {
+  Optional<ento::Loc> ErrnoLoc = getErrnoLoc(State);
+  if (!ErrnoLoc)
+    return State;
+  const MemRegion *ErrnoRegion = ErrnoLoc->getAsRegion();
+
+  // If 'errno' is invalidated we can not know if it is checked or written into,
+  // allow read and write without bug reports.
+  if (llvm::is_contained(Regions, ErrnoRegion))
+    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.
+  if (llvm::is_contained(Regions, ErrnoRegion->getMemorySpace()))
+    return setErrnoStateIrrelevant(State);
+
+  return State;
+}
+
+void ento::registerErrnoChecker(CheckerManager &mgr) {
+  const AnalyzerOptions &Opts = mgr.getAnalyzerOptions();
+  auto *Checker = mgr.registerChecker<ErrnoChecker>();
+  Checker->AllowErrnoReadOutsideConditions = Opts.getCheckerBooleanOption(
+      Checker, "AllowErrnoReadOutsideConditionExpressions");
+}
+
+bool ento::shouldRegisterErrnoChecker(const CheckerManager &mgr) {
+  return true;
+}

diff  --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
index e39f783aa0cf9..618f7e97f6e89 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
@@ -70,6 +70,8 @@ class ErrnoModeling
 /// The value is null if the 'errno' value was not recognized in the AST.
 REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoRegion, const MemRegion *)
 
+REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoState, errno_modeling::ErrnoCheckState)
+
 /// Search for a variable called "errno" in the AST.
 /// Return nullptr if not found.
 static const VarDecl *getErrnoVar(ASTContext &ACtx) {
@@ -141,7 +143,8 @@ void ErrnoModeling::checkBeginFunction(CheckerContext &C) const {
         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::Irrelevant);
     C.addTransition(State);
   } else if (ErrnoDecl) {
     assert(isa<FunctionDecl>(ErrnoDecl) && "Invalid errno location function.");
@@ -168,7 +171,8 @@ void ErrnoModeling::checkBeginFunction(CheckerContext &C) const {
         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::Irrelevant);
     C.addTransition(State);
   }
 }
@@ -195,7 +199,7 @@ bool ErrnoModeling::evalCall(const CallEvent &Call, CheckerContext &C) const {
 void ErrnoModeling::checkLiveSymbols(ProgramStateRef State,
                                      SymbolReaper &SR) const {
   // The special errno region should never garbage collected.
-  if (const MemRegion *ErrnoR = State->get<ErrnoRegion>())
+  if (const auto *ErrnoR = State->get<ErrnoRegion>())
     SR.markLive(ErrnoR);
 }
 
@@ -212,22 +216,63 @@ Optional<SVal> getErrnoValue(ProgramStateRef State) {
 }
 
 ProgramStateRef setErrnoValue(ProgramStateRef State,
-                              const LocationContext *LCtx, SVal Value) {
+                              const LocationContext *LCtx, SVal Value,
+                              ErrnoCheckState EState) {
   const MemRegion *ErrnoR = State->get<ErrnoRegion>();
   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 = State->get<ErrnoRegion>();
   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 = State->get<ErrnoRegion>();
+  if (!ErrnoR)
+    return {};
+  return loc::MemRegionVal{ErrnoR};
+}
+
+ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState) {
+  return State->set<ErrnoState>(EState);
+}
+
+ErrnoCheckState getErrnoState(ProgramStateRef State) {
+  return State->get<ErrnoState>();
+}
+
+bool isErrno(const Decl *D) {
+  if (const auto *VD = dyn_cast_or_null<VarDecl>(D))
+    if (const IdentifierInfo *II = VD->getIdentifier())
+      return II->getName() == ErrnoVarName;
+  if (const auto *FD = dyn_cast_or_null<FunctionDecl>(D))
+    if (const IdentifierInfo *II = FD->getIdentifier())
+      return llvm::is_contained(ErrnoLocationFuncNames, II->getName());
+  return false;
+}
+
+const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) {
+  return C.getNoteTag([Message](PathSensitiveBugReport &BR) -> std::string {
+    const MemRegion *ErrnoR = BR.getErrorNode()->getState()->get<ErrnoRegion>();
+    if (ErrnoR && BR.isInteresting(ErrnoR)) {
+      BR.markNotInteresting(ErrnoR);
+      return Message;
+    }
+    return "";
+  });
 }
 
 } // namespace errno_modeling

diff  --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h
index 97ef3501f3c97..3757e25e1afe6 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h
+++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h
@@ -21,16 +21,55 @@ namespace clang {
 namespace ento {
 namespace errno_modeling {
 
+enum ErrnoCheckState : unsigned {
+  /// We do not know anything about 'errno'.
+  Irrelevant = 0,
+
+  /// Value of 'errno' should be checked to find out if a previous function call
+  /// has failed.
+  MustBeChecked = 1,
+
+  /// Value of 'errno' is not allowed to be read, it can contain an unspecified
+  /// value.
+  MustNotBeChecked = 2
+};
+
 /// Returns the value of 'errno', if 'errno' was found in the AST.
 llvm::Optional<SVal> getErrnoValue(ProgramStateRef State);
 
+/// Returns the errno check state, \c Errno_Irrelevant if 'errno' was not found
+/// (this is not the only case for that value).
+ErrnoCheckState getErrnoState(ProgramStateRef State);
+
+/// Returns the location that points to the \c MemoryRegion where the 'errno'
+/// value is stored. Returns \c None if 'errno' was not found. Otherwise it
+/// always returns a valid memory region in the system global memory space.
+llvm::Optional<Loc> getErrnoLoc(ProgramStateRef State);
+
 /// Set value of 'errno' to any SVal, if possible.
+/// The errno check state is set always when the 'errno' value is set.
 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.
+/// The errno check state is set always when the 'errno' value is set.
 ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C,
-                              uint64_t Value);
+                              uint64_t Value, ErrnoCheckState EState);
+
+/// Set the errno check state, do not modify the errno value.
+ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState);
+
+/// Determine if a `Decl` node related to 'errno'.
+/// This is true if the declaration is the errno variable or a function
+/// that returns a pointer to the 'errno' value (usually the 'errno' macro is
+/// defined with this function). \p D is not required to be a canonical
+/// declaration.
+bool isErrno(const Decl *D);
+
+/// Create a NoteTag that displays the message if the 'errno' memory region is
+/// marked as interesting, and resets the interestingness.
+const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message);
 
 } // namespace errno_modeling
 } // namespace ento

diff  --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp
index 8177923b9a924..924407ebe2d18 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp
@@ -20,6 +20,7 @@
 
 using namespace clang;
 using namespace ento;
+using namespace errno_modeling;
 
 namespace {
 
@@ -28,11 +29,43 @@ class ErrnoTesterChecker : public Checker<eval::Call> {
   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
 
 private:
+  /// Evaluate function \code void ErrnoTesterChecker_setErrno(int) \endcode.
+  /// Set value of \c errno to the argument.
   static void evalSetErrno(CheckerContext &C, const CallEvent &Call);
+  /// Evaluate function \code int ErrnoTesterChecker_getErrno() \endcode.
+  /// Return the value of \c errno.
   static void evalGetErrno(CheckerContext &C, const CallEvent &Call);
+  /// Evaluate function \code int ErrnoTesterChecker_setErrnoIfError() \endcode.
+  /// Simulate a standard library function tha returns 0 on success and 1 on
+  /// failure. On the success case \c errno is not allowed to be used (may be
+  /// undefined). On the failure case \c errno is set to a fixed value 11 and
+  /// is not needed to be checked.
   static void evalSetErrnoIfError(CheckerContext &C, const CallEvent &Call);
+  /// Evaluate function \code int ErrnoTesterChecker_setErrnoIfErrorRange()
+  /// \endcode. Same as \c ErrnoTesterChecker_setErrnoIfError but \c errno is
+  /// set to a range (to be nonzero) at the failure case.
   static void evalSetErrnoIfErrorRange(CheckerContext &C,
                                        const CallEvent &Call);
+  /// Evaluate function \code int ErrnoTesterChecker_setErrnoCheckState()
+  /// \endcode. This function simulates the following:
+  /// - Return 0 and leave \c errno with undefined value.
+  ///   This is the case of a successful standard function call.
+  ///   For example if \c ftell returns not -1.
+  /// - Return 1 and sets \c errno to a specific error code (1).
+  ///   This is the case of a failed standard function call.
+  ///   The function indicates the failure by a special return value
+  ///   that is returned only at failure.
+  ///   \c errno can be checked but it is not required.
+  ///   For example if \c ftell returns -1.
+  /// - Return 2 and may set errno to a value (actually it does not set it).
+  ///   This is the case of a standard function call where the failure can only
+  ///   be checked by reading from \c errno. The value of \c errno is changed by
+  ///   the function only at failure, the user should set \c errno to 0 before
+  ///   the call (\c ErrnoChecker does not check for this rule).
+  ///   \c strtol is an example of this case, if it returns \c LONG_MIN (or
+  ///   \c LONG_MAX). This case applies only if \c LONG_MIN or \c LONG_MAX is
+  ///   returned, otherwise the first case in this list applies.
+  static void evalSetErrnoCheckState(CheckerContext &C, const CallEvent &Call);
 
   using EvalFn = std::function<void(CheckerContext &, const CallEvent &)>;
   const CallDescriptionMap<EvalFn> TestCalls{
@@ -41,22 +74,24 @@ class ErrnoTesterChecker : public Checker<eval::Call> {
       {{"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), 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);
@@ -71,10 +106,11 @@ void ErrnoTesterChecker::evalSetErrnoIfError(CheckerContext &C,
 
   ProgramStateRef StateSuccess = State->BindExpr(
       Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));
+  StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);
 
   ProgramStateRef StateFailure = State->BindExpr(
       Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
-  StateFailure = errno_modeling::setErrnoValue(StateFailure, C, 11);
+  StateFailure = setErrnoValue(StateFailure, C, 11, Irrelevant);
 
   C.addTransition(StateSuccess);
   C.addTransition(StateFailure);
@@ -87,6 +123,7 @@ void ErrnoTesterChecker::evalSetErrnoIfErrorRange(CheckerContext &C,
 
   ProgramStateRef StateSuccess = State->BindExpr(
       Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));
+  StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);
 
   ProgramStateRef StateFailure = State->BindExpr(
       Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
@@ -94,13 +131,40 @@ void ErrnoTesterChecker::evalSetErrnoIfErrorRange(CheckerContext &C,
       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, 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 = setErrnoState(StateSuccess, MustNotBeChecked);
+
+  ProgramStateRef StateFailure1 = State->BindExpr(
+      Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
+  StateFailure1 = setErrnoValue(StateFailure1, C, 1, Irrelevant);
+
+  ProgramStateRef StateFailure2 = State->BindExpr(
+      Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(2, true));
+  StateFailure2 = setErrnoValue(StateFailure2, C, 2, MustBeChecked);
+
+  C.addTransition(StateSuccess,
+                  getErrnoNoteTag(C, "Assuming that this function succeeds but "
+                                     "sets 'errno' to an unspecified value."));
+  C.addTransition(StateFailure1);
+  C.addTransition(
+      StateFailure2,
+      getErrnoNoteTag(C, "Assuming that this function returns 2. 'errno' "
+                         "should be checked to test for failure."));
+}
+
 bool ErrnoTesterChecker::evalCall(const CallEvent &Call,
                                   CheckerContext &C) const {
   const EvalFn *Fn = TestCalls.lookup(Call);

diff  --git a/clang/test/Analysis/analyzer-config.c b/clang/test/Analysis/analyzer-config.c
index 9f22be6742c1f..e06a8ae5604fb 100644
--- a/clang/test/Analysis/analyzer-config.c
+++ b/clang/test/Analysis/analyzer-config.c
@@ -12,6 +12,7 @@
 // CHECK-NEXT: alpha.security.MmapWriteExec:MmapProtExec = 0x04
 // CHECK-NEXT: alpha.security.MmapWriteExec:MmapProtRead = 0x01
 // CHECK-NEXT: alpha.security.taint.TaintPropagation:Config = ""
+// CHECK-NEXT: alpha.unix.Errno:AllowErrnoReadOutsideConditionExpressions = true
 // CHECK-NEXT: apiModeling.StdCLibraryFunctions:DisplayLoadedSummaries = false
 // CHECK-NEXT: apiModeling.StdCLibraryFunctions:ModelPOSIX = false
 // CHECK-NEXT: apply-fixits = false

diff  --git a/clang/test/Analysis/errno-notes.c b/clang/test/Analysis/errno-notes.c
new file mode 100644
index 0000000000000..711acc0f5db81
--- /dev/null
+++ b/clang/test/Analysis/errno-notes.c
@@ -0,0 +1,62 @@
+// RUN: %clang_analyze_cc1 -verify -analyzer-output text %s \
+// RUN:   -analyzer-checker=core \
+// 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 -analyzer-output text %s \
+// RUN:   -analyzer-checker=core \
+// 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/errno_var.h"
+#include "Inputs/system-header-simulator.h"
+#ifdef ERRNO_VAR
+#include "Inputs/errno_var.h"
+#endif
+#ifdef ERRNO_FUNC
+#include "Inputs/errno_func.h"
+#endif
+
+int ErrnoTesterChecker_setErrnoCheckState();
+
+void something();
+
+void testErrnoCheckUndefRead() {
+  int X = ErrnoTesterChecker_setErrnoCheckState();
+  something();
+  X = ErrnoTesterChecker_setErrnoCheckState(); // expected-note{{Assuming that this function succeeds but sets 'errno' to an unspecified value}}
+  if (X == 0) {                                // expected-note{{'X' is equal to 0}}
+                                               // expected-note at -1{{Taking true branch}}
+    if (errno) {
+    } // expected-warning at -1{{An undefined value may be read from 'errno'}}
+      // expected-note at -2{{An undefined value may be read from 'errno'}}
+  }
+}
+
+void testErrnoCheckOverwrite() {
+  int X = ErrnoTesterChecker_setErrnoCheckState();
+  something();
+  X = ErrnoTesterChecker_setErrnoCheckState(); // expected-note{{Assuming that this function returns 2. 'errno' should be checked to test for failure}}
+  if (X == 2) {                                // expected-note{{'X' is equal to 2}}
+                                               // expected-note at -1{{Taking true branch}}
+    errno = 0;                                 // expected-warning{{Value of 'errno' was not checked and is overwritten here}}
+                                               // expected-note at -1{{Value of 'errno' was not checked and is overwritten here}}
+  }
+}
+
+void testErrnoCheckOverwriteStdCall() {
+  int X = ErrnoTesterChecker_setErrnoCheckState();
+  something();
+  X = ErrnoTesterChecker_setErrnoCheckState(); // expected-note{{Assuming that this function returns 2. 'errno' should be checked to test for failure}}
+  if (X == 2) {                                // expected-note{{'X' is equal to 2}}
+                                               // expected-note at -1{{Taking true branch}}
+    printf("");                                // expected-warning{{Value of 'errno' was not checked and may be overwritten by function 'printf'}}
+                                               // expected-note at -1{{Value of 'errno' was not checked and may be overwritten by function 'printf'}}
+  }
+}

diff  --git a/clang/test/Analysis/errno-options.c b/clang/test/Analysis/errno-options.c
new file mode 100644
index 0000000000000..0449f22b8f7b4
--- /dev/null
+++ b/clang/test/Analysis/errno-options.c
@@ -0,0 +1,55 @@
+// RUN: %clang_analyze_cc1 -verify %s \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-checker=apiModeling.Errno \
+// RUN:   -analyzer-checker=debug.ErrnoTest \
+// RUN:   -analyzer-checker=alpha.unix.Errno \
+// RUN:   -analyzer-config alpha.unix.Errno:AllowErrnoReadOutsideConditionExpressions=false \
+// RUN:   -DERRNO_VAR
+
+// RUN: %clang_analyze_cc1 -verify %s \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-checker=apiModeling.Errno \
+// RUN:   -analyzer-checker=debug.ErrnoTest \
+// RUN:   -analyzer-checker=alpha.unix.Errno \
+// RUN:   -analyzer-config alpha.unix.Errno:AllowErrnoReadOutsideConditionExpressions=false \
+// RUN:   -DERRNO_FUNC
+
+#include "Inputs/system-header-simulator.h"
+#ifdef ERRNO_VAR
+#include "Inputs/errno_var.h"
+#endif
+#ifdef ERRNO_FUNC
+#include "Inputs/errno_func.h"
+#endif
+
+int ErrnoTesterChecker_setErrnoIfError();
+
+void test_cond() {
+  ErrnoTesterChecker_setErrnoIfError();
+  int A = errno ? 1 : 2;
+  // expected-warning at -1{{An undefined value may be read from 'errno'}}
+}
+
+void test_errno_store_into_variable() {
+  ErrnoTesterChecker_setErrnoIfError();
+  int a = errno;
+  // expected-warning at -1{{An undefined value may be read from 'errno'}}
+}
+
+void test_errno_store_into_variable_in_expr() {
+  ErrnoTesterChecker_setErrnoIfError();
+  int a = 4 + errno;
+  // expected-warning at -1{{An undefined value may be read from 'errno'}}
+}
+
+int test_errno_return() {
+  ErrnoTesterChecker_setErrnoIfError();
+  return errno;
+  // expected-warning at -1{{An undefined value may be read from 'errno'}}
+}
+
+int test_errno_return_expr() {
+  ErrnoTesterChecker_setErrnoIfError();
+  return errno > 10;
+  // expected-warning at -1{{An undefined value may be read from 'errno'}}
+}

diff  --git a/clang/test/Analysis/errno.c b/clang/test/Analysis/errno.c
index 3bfe19a287d1b..1f094cc741eb3 100644
--- a/clang/test/Analysis/errno.c
+++ b/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 @@ void ErrnoTesterChecker_setErrno(int);
 int ErrnoTesterChecker_getErrno();
 int ErrnoTesterChecker_setErrnoIfError();
 int ErrnoTesterChecker_setErrnoIfErrorRange();
+int ErrnoTesterChecker_setErrnoCheckState();
 
 void something();
 
@@ -61,3 +65,199 @@ void testIfErrorRange() {
     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{{An undefined value may be read from 'errno' [alpha.unix.Errno]}}
+    }
+    if (errno) { // no warning for second time (analysis stops at the first warning)
+    }
+  }
+  X = ErrnoTesterChecker_setErrnoCheckState();
+  if (X == 0) {
+    if (errno) { // expected-warning{{An undefined value may be read from 'errno' [alpha.unix.Errno]}}
+    }
+    errno = 0;
+  }
+  X = ErrnoTesterChecker_setErrnoCheckState();
+  if (X == 0) {
+    errno = 0;
+    if (errno) { // no warning after overwritten '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.
+  int X = ErrnoTesterChecker_setErrnoCheckState();
+  if (X == 1) {
+    if (errno) { // no warning
+    }
+  }
+  X = ErrnoTesterChecker_setErrnoCheckState();
+  if (X == 1) {
+    errno = 0; // no warning
+  }
+}
+
+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.
+  // The same applies to other functions that can indicate failure only by
+  // change of 'errno'.
+  int X = ErrnoTesterChecker_setErrnoCheckState();
+  if (X == 2) {
+    errno = 0; // expected-warning{{Value of 'errno' was not checked and is overwritten here [alpha.unix.Errno]}}
+    errno = 0;
+  }
+  X = ErrnoTesterChecker_setErrnoCheckState();
+  if (X == 2) {
+    errno = 0; // expected-warning{{Value of 'errno' was not checked and is overwritten here [alpha.unix.Errno]}}
+    if (errno) {
+    }
+  }
+}
+
+void testErrnoCheck3() {
+  int X = ErrnoTesterChecker_setErrnoCheckState();
+  if (X == 2) {
+    if (errno) {
+    }
+    errno = 0; // no warning after 'errno' was read
+  }
+  X = ErrnoTesterChecker_setErrnoCheckState();
+  if (X == 2) {
+    int A = errno;
+    errno = 0; // no warning after 'errno' was read
+  }
+}
+
+void testErrnoCheckUndefinedLoad() {
+  int X = ErrnoTesterChecker_setErrnoCheckState();
+  if (X == 0) {
+    if (errno) { // expected-warning{{An undefined value may be read from 'errno' [alpha.unix.Errno]}}
+    }
+  }
+}
+
+void testErrnoNotCheckedAtSystemCall() {
+  int X = ErrnoTesterChecker_setErrnoCheckState();
+  if (X == 2) {
+    printf("%i", 1); // expected-warning{{Value of 'errno' was not checked and may be overwritten by function 'printf' [alpha.unix.Errno]}}
+    printf("%i", 1); // no warning ('printf' does not change errno state)
+  }
+}
+
+void testErrnoCheckStateInvalidate() {
+  int X = ErrnoTesterChecker_setErrnoCheckState();
+  if (X == 0) {
+    something();
+    if (errno) { // no warning after an invalidating function call
+    }
+  }
+  X = ErrnoTesterChecker_setErrnoCheckState();
+  if (X == 0) {
+    printf("%i", 1);
+    if (errno) { // no warning after an invalidating standard function call
+    }
+  }
+}
+
+void testErrnoCheckStateInvalidate1() {
+  int X = ErrnoTesterChecker_setErrnoCheckState();
+  if (X == 2) {
+    clang_analyzer_eval(errno); // expected-warning{{TRUE}}
+    something();
+    clang_analyzer_eval(errno); // expected-warning{{UNKNOWN}}
+    errno = 0;                  // no warning after invalidation
+  }
+}
+
+void test_if_cond_in_expr() {
+  ErrnoTesterChecker_setErrnoIfError();
+  if (errno + 10 > 2) {
+    // expected-warning at -1{{An undefined value may be read from 'errno'}}
+  }
+}
+
+void test_for_cond() {
+  ErrnoTesterChecker_setErrnoIfError();
+  for (; errno != 0;) {
+  // expected-warning at -1{{An undefined value may be read from 'errno'}}
+  }
+}
+
+void test_do_cond() {
+  ErrnoTesterChecker_setErrnoIfError();
+  do {
+  } while (errno != 0);
+  // expected-warning at -1{{An undefined value may be read from 'errno'}}
+}
+
+void test_while_cond() {
+  ErrnoTesterChecker_setErrnoIfError();
+  while (errno != 0) {
+  // expected-warning at -1{{An undefined value may be read from 'errno'}}
+  }
+}
+
+void test_switch_cond() {
+  ErrnoTesterChecker_setErrnoIfError();
+  switch (errno) {}
+  // expected-warning at -1{{An undefined value may be read from 'errno'}}
+}
+
+void test_conditional_cond() {
+  ErrnoTesterChecker_setErrnoIfError();
+  int A = errno ? 1 : 2;
+  // expected-warning at -1{{An undefined value may be read from 'errno'}}
+}
+
+void test_binary_conditional_cond() {
+  ErrnoTesterChecker_setErrnoIfError();
+  int A = errno ?: 2;
+  // expected-warning at -1{{An undefined value may be read from 'errno'}}
+}
+
+void test_errno_store_into_variable() {
+  ErrnoTesterChecker_setErrnoIfError();
+  int a = errno; // AllowNonConditionErrnoRead is on by default, no warning
+}
+
+void test_errno_store_into_variable_in_expr() {
+  ErrnoTesterChecker_setErrnoIfError();
+  int a = errno > 1; // AllowNonConditionErrnoRead is on by default, no warning
+}
+
+int test_errno_return() {
+  ErrnoTesterChecker_setErrnoIfError();
+  return errno;
+}
+
+void test_errno_pointer1() {
+  ErrnoTesterChecker_setErrnoIfError();
+  int *ErrnoP = &errno;
+  int A = errno ? 1 : 2;
+  // expected-warning at -1{{An undefined value may be read from 'errno'}}
+}
+
+void test_errno_pointer2() {
+  ErrnoTesterChecker_setErrnoIfError();
+  int *ErrnoP = &errno;
+  int A = (*ErrnoP) ? 1 : 2;
+  // expected-warning at -1{{An undefined value may be read from 'errno'}}
+}
+
+int f(int);
+
+void test_errno_in_condition_in_function_call() {
+  ErrnoTesterChecker_setErrnoIfError();
+  if (f(errno) != 0) {
+  }
+}


        


More information about the cfe-commits mailing list