aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp')
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp185
1 files changed, 185 insertions, 0 deletions
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp
new file mode 100644
index 000000000000..c46ebee0c94f
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp
@@ -0,0 +1,185 @@
+//=== 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"
+#include <optional>
+
+using namespace clang;
+using namespace ento;
+using namespace errno_modeling;
+
+namespace {
+
+class ErrnoTesterChecker : public Checker<eval::Call> {
+public:
+ 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{
+ {{{"ErrnoTesterChecker_setErrno"}, 1}, &ErrnoTesterChecker::evalSetErrno},
+ {{{"ErrnoTesterChecker_getErrno"}, 0}, &ErrnoTesterChecker::evalGetErrno},
+ {{{"ErrnoTesterChecker_setErrnoIfError"}, 0},
+ &ErrnoTesterChecker::evalSetErrnoIfError},
+ {{{"ErrnoTesterChecker_setErrnoIfErrorRange"}, 0},
+ &ErrnoTesterChecker::evalSetErrnoIfErrorRange},
+ {{{"ErrnoTesterChecker_setErrnoCheckState"}, 0},
+ &ErrnoTesterChecker::evalSetErrnoCheckState}};
+};
+
+} // namespace
+
+void ErrnoTesterChecker::evalSetErrno(CheckerContext &C,
+ const CallEvent &Call) {
+ C.addTransition(setErrnoValue(C.getState(), C.getLocationContext(),
+ Call.getArgSVal(0), Irrelevant));
+}
+
+void ErrnoTesterChecker::evalGetErrno(CheckerContext &C,
+ const CallEvent &Call) {
+ ProgramStateRef State = C.getState();
+
+ std::optional<SVal> ErrnoVal = 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));
+ StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);
+
+ ProgramStateRef StateFailure = State->BindExpr(
+ Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
+ StateFailure = setErrnoValue(StateFailure, C, 11, Irrelevant);
+
+ 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));
+ StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);
+
+ 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 =
+ 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);
+ 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;
+}