mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-23 22:00:10 +00:00
[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
This commit is contained in:
parent
4429cf146e
commit
d8a2afb244
@ -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>;
|
||||
|
@ -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 @@ public:
|
||||
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);
|
||||
|
@ -40,6 +40,8 @@ add_clang_library(clangStaticAnalyzerCheckers
|
||||
DynamicTypePropagation.cpp
|
||||
DynamicTypeChecker.cpp
|
||||
EnumCastOutOfRangeChecker.cpp
|
||||
ErrnoModeling.cpp
|
||||
ErrnoTesterChecker.cpp
|
||||
ExprInspectionChecker.cpp
|
||||
FixedAddressChecker.cpp
|
||||
FuchsiaHandleChecker.cpp
|
||||
|
249
clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
Normal file
249
clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
Normal file
@ -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::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.
|
||||
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<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);
|
||||
}
|
||||
|
||||
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<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;
|
||||
}
|
39
clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h
Normal file
39
clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h
Normal file
@ -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<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_ERRNOMODELING_H
|
120
clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp
Normal file
120
clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp
Normal file
@ -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<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;
|
||||
}
|
@ -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) {
|
||||
|
5
clang/test/Analysis/Inputs/errno_func.h
Normal file
5
clang/test/Analysis/Inputs/errno_func.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma clang system_header
|
||||
|
||||
// Define 'errno' as a macro that calls a function.
|
||||
int *__errno_location();
|
||||
#define errno (*__errno_location())
|
5
clang/test/Analysis/Inputs/errno_var.h
Normal file
5
clang/test/Analysis/Inputs/errno_var.h
Normal file
@ -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;
|
@ -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);
|
||||
|
@ -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
|
||||
|
63
clang/test/Analysis/errno.c
Normal file
63
clang/test/Analysis/errno.c
Normal file
@ -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}}
|
||||
}
|
||||
}
|
49
clang/test/Analysis/global-region-invalidation-errno.c
Normal file
49
clang/test/Analysis/global-region-invalidation-errno.c
Normal file
@ -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;
|
||||
}
|
@ -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 @@ int stdinTest(void) {
|
||||
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 @@ void SetToNonZero(void) {
|
||||
static int g = 5;
|
||||
clang_analyzer_eval(g == 5); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user