[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:
Balázs Kéri 2022-02-25 10:15:06 +01:00
parent 4429cf146e
commit d8a2afb244
15 changed files with 556 additions and 37 deletions

View File

@ -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>;

View File

@ -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);

View File

@ -40,6 +40,8 @@ add_clang_library(clangStaticAnalyzerCheckers
DynamicTypePropagation.cpp
DynamicTypeChecker.cpp
EnumCastOutOfRangeChecker.cpp
ErrnoModeling.cpp
ErrnoTesterChecker.cpp
ExprInspectionChecker.cpp
FixedAddressChecker.cpp
FuchsiaHandleChecker.cpp

View 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;
}

View 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

View 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;
}

View File

@ -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) {

View 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())

View 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;

View File

@ -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);

View File

@ -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

View 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}}
}
}

View 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;
}

View File

@ -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}}
}

View File

@ -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