[clang][analyzer] Extend StreamChecker with some new functions.

The stream handling functions `ftell`, `rewind`, `fgetpos`, `fsetpos`
are evaluated in the checker more exactly than before.
New tests are added to test behavior of the checker together with
StdLibraryFunctionsChecker. The option ModelPOSIX of that checker
affects if (most of) the stream functions are recognized, and checker
StdLibraryFunctionArgs generates warnings if constraints for arguments
are not satisfied. The state of `errno` is set by StdLibraryFunctionsChecker
too for every case in the stream functions.
StreamChecker works with the stream state only, does not set the errno state,
and is not dependent on other checkers.

Reviewed By: Szelethus

Differential Revision: https://reviews.llvm.org/D140395
This commit is contained in:
Balázs Kéri 2023-01-06 11:21:41 +01:00
parent 5867241eac
commit 5cf85323a0
6 changed files with 700 additions and 16 deletions

View File

@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
@ -85,10 +86,10 @@ const StreamErrorState ErrorFError{false, false, true};
/// Full state information about a stream pointer.
struct StreamState {
/// The last file operation called in the stream.
/// Can be nullptr.
const FnDescription *LastOperation;
/// State of a stream symbol.
/// FIXME: We need maybe an "escaped" state later.
enum KindTy {
Opened, /// Stream is opened.
Closed, /// Closed stream (an invalid stream pointer after it was closed).
@ -202,7 +203,7 @@ ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C,
ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State,
CheckerContext &C, const CallExpr *CE) {
State = State->BindExpr(CE, C.getLocationContext(),
C.getSValBuilder().makeIntVal(Value, false));
C.getSValBuilder().makeIntVal(Value, CE->getType()));
return State;
}
@ -250,10 +251,14 @@ private:
std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}},
{{{"fseek"}, 3},
{&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
{{{"ftell"}, 1}, {&StreamChecker::preDefault, nullptr, 0}},
{{{"rewind"}, 1}, {&StreamChecker::preDefault, nullptr, 0}},
{{{"fgetpos"}, 2}, {&StreamChecker::preDefault, nullptr, 0}},
{{{"fsetpos"}, 2}, {&StreamChecker::preDefault, nullptr, 0}},
{{{"ftell"}, 1},
{&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}},
{{{"rewind"}, 1},
{&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}},
{{{"fgetpos"}, 2},
{&StreamChecker::preDefault, &StreamChecker::evalFgetpos, 0}},
{{{"fsetpos"}, 2},
{&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}},
{{{"clearerr"}, 1},
{&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}},
{{{"feof"}, 1},
@ -279,6 +284,8 @@ private:
0}},
};
mutable Optional<int> EofVal;
void evalFopen(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
@ -304,6 +311,18 @@ private:
void evalFseek(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
void evalFgetpos(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
void evalFsetpos(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
void evalFtell(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
void evalRewind(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
void preDefault(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
@ -412,6 +431,17 @@ private:
});
}
void initEof(CheckerContext &C) const {
if (EofVal)
return;
if (const llvm::Optional<int> OptInt =
tryExpandAsInteger("EOF", C.getPreprocessor()))
EofVal = *OptInt;
else
EofVal = -1;
}
/// Searches for the ExplodedNode where the file descriptor was acquired for
/// StreamSym.
static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N,
@ -427,8 +457,7 @@ private:
REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
inline void assertStreamStateOpened(const StreamState *SS) {
assert(SS->isOpened() &&
"Previous create of error node for non-opened stream failed?");
assert(SS->isOpened() && "Stream is expected to be opened");
}
const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N,
@ -458,6 +487,8 @@ const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N,
void StreamChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
initEof(C);
const FnDescription *Desc = lookupFn(Call);
if (!Desc || !Desc->PreFn)
return;
@ -575,6 +606,10 @@ void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
if (!SS)
return;
auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
if (!CE)
return;
assertStreamStateOpened(SS);
// Close the File Descriptor.
@ -582,7 +617,16 @@ void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
// and can not be used any more.
State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc));
C.addTransition(State);
// Return 0 on success, EOF on failure.
SValBuilder &SVB = C.getSValBuilder();
ProgramStateRef StateSuccess = State->BindExpr(
CE, C.getLocationContext(), SVB.makeIntVal(0, C.getASTContext().IntTy));
ProgramStateRef StateFailure =
State->BindExpr(CE, C.getLocationContext(),
SVB.makeIntVal(*EofVal, C.getASTContext().IntTy));
C.addTransition(StateSuccess);
C.addTransition(StateFailure);
}
void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call,
@ -767,6 +811,131 @@ void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call,
C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
}
void StreamChecker::evalFgetpos(const FnDescription *Desc,
const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
if (!Sym)
return;
// Do not evaluate if stream is not found.
if (!State->get<StreamMap>(Sym))
return;
auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
if (!CE)
return;
DefinedSVal RetVal = makeRetVal(C, CE);
State = State->BindExpr(CE, C.getLocationContext(), RetVal);
ProgramStateRef StateNotFailed, StateFailed;
std::tie(StateFailed, StateNotFailed) =
C.getConstraintManager().assumeDual(State, RetVal);
// This function does not affect the stream state.
// Still we add success and failure state with the appropriate return value.
// StdLibraryFunctionsChecker can change these states (set the 'errno' state).
C.addTransition(StateNotFailed);
C.addTransition(StateFailed);
}
void StreamChecker::evalFsetpos(const FnDescription *Desc,
const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
if (!StreamSym)
return;
const StreamState *SS = State->get<StreamMap>(StreamSym);
if (!SS)
return;
auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
if (!CE)
return;
assertStreamStateOpened(SS);
DefinedSVal RetVal = makeRetVal(C, CE);
State = State->BindExpr(CE, C.getLocationContext(), RetVal);
ProgramStateRef StateNotFailed, StateFailed;
std::tie(StateFailed, StateNotFailed) =
C.getConstraintManager().assumeDual(State, RetVal);
StateNotFailed = StateNotFailed->set<StreamMap>(
StreamSym, StreamState::getOpened(Desc, ErrorNone, false));
// At failure ferror could be set.
// The standards do not tell what happens with the file position at failure.
// But we can assume that it is dangerous to make a next I/O operation after
// the position was not set correctly (similar to 'fseek').
StateFailed = StateFailed->set<StreamMap>(
StreamSym, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true));
C.addTransition(StateNotFailed);
C.addTransition(StateFailed);
}
void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
if (!Sym)
return;
if (!State->get<StreamMap>(Sym))
return;
auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
if (!CE)
return;
SValBuilder &SVB = C.getSValBuilder();
NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
ProgramStateRef StateNotFailed =
State->BindExpr(CE, C.getLocationContext(), RetVal);
auto Cond = SVB.evalBinOp(State, BO_GE, RetVal,
SVB.makeZeroVal(C.getASTContext().LongTy),
SVB.getConditionType())
.getAs<DefinedOrUnknownSVal>();
if (!Cond)
return;
StateNotFailed = StateNotFailed->assume(*Cond, true);
if (!StateNotFailed)
return;
ProgramStateRef StateFailed = State->BindExpr(
CE, C.getLocationContext(), SVB.makeIntVal(-1, C.getASTContext().LongTy));
C.addTransition(StateNotFailed);
C.addTransition(StateFailed);
}
void StreamChecker::evalRewind(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
if (!StreamSym)
return;
const StreamState *SS = State->get<StreamMap>(StreamSym);
if (!SS)
return;
auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
if (!CE)
return;
assertStreamStateOpened(SS);
State = State->set<StreamMap>(StreamSym,
StreamState::getOpened(Desc, ErrorNone, false));
C.addTransition(State);
}
void StreamChecker::evalClearerr(const FnDescription *Desc,
const CallEvent &Call,
CheckerContext &C) const {

View File

@ -42,9 +42,9 @@ FILE *funopen(const void *,
fpos_t (*)(void *, fpos_t, int),
int (*)(void *));
FILE *fopen(const char *path, const char *mode);
FILE *fopen(const char *restrict path, const char *restrict mode);
FILE *tmpfile(void);
FILE *freopen(const char *pathname, const char *mode, FILE *stream);
FILE *freopen(const char *restrict pathname, const char *restrict mode, FILE *restrict stream);
int fclose(FILE *fp);
size_t fread(void *restrict, size_t, size_t, FILE *restrict);
size_t fwrite(const void *restrict, size_t, size_t, FILE *restrict);
@ -52,7 +52,7 @@ int fputc(int ch, FILE *stream);
int fseek(FILE *__stream, long int __off, int __whence);
long int ftell(FILE *__stream);
void rewind(FILE *__stream);
int fgetpos(FILE *stream, fpos_t *pos);
int fgetpos(FILE *restrict stream, fpos_t *restrict pos);
int fsetpos(FILE *stream, const fpos_t *pos);
void clearerr(FILE *stream);
int feof(FILE *stream);

View File

@ -0,0 +1,128 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=core \
// RUN: -analyzer-checker=alpha.unix.Stream \
// RUN: -analyzer-checker=alpha.unix.Errno \
// RUN: -analyzer-checker=apiModeling.StdCLibraryFunctions \
// RUN: -analyzer-config apiModeling.StdCLibraryFunctions:ModelPOSIX=true \
// RUN: -analyzer-output text -verify %s
#include "Inputs/system-header-simulator.h"
#include "Inputs/errno_func.h"
void check_fopen(void) {
FILE *F = fopen("xxx", "r");
// expected-note@-1{{Assuming that function 'fopen' is successful, in this case the value 'errno' may be undefined after the call and should not be used}}
// expected-note@+2{{'F' is non-null}}
// expected-note@+1{{Taking false branch}}
if (!F)
return;
if (errno) {} // expected-warning{{An undefined value may be read from 'errno' [alpha.unix.Errno]}}
// expected-note@-1{{An undefined value may be read from 'errno'}}
fclose(F);
}
void check_tmpfile(void) {
FILE *F = tmpfile();
// expected-note@-1{{Assuming that function 'tmpfile' is successful, in this case the value 'errno' may be undefined after the call and should not be used}}
// expected-note@+2{{'F' is non-null}}
// expected-note@+1{{Taking false branch}}
if (!F)
return;
if (errno) {} // expected-warning{{An undefined value may be read from 'errno' [alpha.unix.Errno]}}
// expected-note@-1{{An undefined value may be read from 'errno'}}
fclose(F);
}
void check_freopen(void) {
FILE *F = tmpfile();
// expected-note@+2{{'F' is non-null}}
// expected-note@+1{{Taking false branch}}
if (!F)
return;
F = freopen("xxx", "w", F);
// expected-note@-1{{Assuming that function 'freopen' is successful}}
// expected-note@+2{{'F' is non-null}}
// expected-note@+1{{Taking false branch}}
if (!F)
return;
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
// expected-note@-1{{An undefined value may be read from 'errno'}}
fclose(F);
}
void check_fclose(void) {
FILE *F = tmpfile();
// expected-note@+2{{'F' is non-null}}
// expected-note@+1{{Taking false branch}}
if (!F)
return;
(void)fclose(F);
// expected-note@-1{{Assuming that function 'fclose' is successful}}
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
// expected-note@-1{{An undefined value may be read from 'errno'}}
}
void check_fread(void) {
char Buf[10];
FILE *F = tmpfile();
// expected-note@+2{{'F' is non-null}}
// expected-note@+1{{Taking false branch}}
if (!F)
return;
(void)fread(Buf, 1, 10, F);
// expected-note@-1{{Assuming that function 'fread' is successful}}
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
// expected-note@-1{{An undefined value may be read from 'errno'}}
(void)fclose(F);
}
void check_fwrite(void) {
char Buf[] = "0123456789";
FILE *F = tmpfile();
// expected-note@+2{{'F' is non-null}}
// expected-note@+1{{Taking false branch}}
if (!F)
return;
int R = fwrite(Buf, 1, 10, F);
// expected-note@-1{{Assuming that function 'fwrite' is successful}}
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
// expected-note@-1{{An undefined value may be read from 'errno'}}
(void)fclose(F);
}
void check_fseek(void) {
FILE *F = tmpfile();
// expected-note@+2{{'F' is non-null}}
// expected-note@+1{{Taking false branch}}
if (!F)
return;
(void)fseek(F, 11, SEEK_SET);
// expected-note@-1{{Assuming that function 'fseek' is successful}}
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
// expected-note@-1{{An undefined value may be read from 'errno'}}
(void)fclose(F);
}
void check_rewind_errnocheck(void) {
FILE *F = tmpfile();
// expected-note@+2{{'F' is non-null}}
// expected-note@+1{{Taking false branch}}
if (!F)
return;
errno = 0;
rewind(F); // expected-note{{Function 'rewind' indicates failure only by setting of 'errno'}}
fclose(F); // expected-warning{{Value of 'errno' was not checked and may be overwritten by function 'fclose' [alpha.unix.Errno]}}
// expected-note@-1{{Value of 'errno' was not checked and may be overwritten by function 'fclose'}}
}
void check_fileno(void) {
FILE *F = tmpfile();
// expected-note@+2{{'F' is non-null}}
// expected-note@+1{{Taking false branch}}
if (!F)
return;
fileno(F);
// expected-note@-1{{Assuming that function 'fileno' is successful}}
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
// expected-note@-1{{An undefined value may be read from 'errno'}}
(void)fclose(F);
}

View File

@ -0,0 +1,224 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.unix.Stream,alpha.unix.Errno,apiModeling.StdCLibraryFunctions,debug.ExprInspection \
// RUN: -analyzer-config apiModeling.StdCLibraryFunctions:ModelPOSIX=true -verify %s
#include "Inputs/system-header-simulator.h"
#include "Inputs/errno_func.h"
extern void clang_analyzer_eval(int);
extern void clang_analyzer_dump(int);
extern void clang_analyzer_printState();
void check_fopen(void) {
FILE *F = fopen("xxx", "r");
if (!F) {
clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
if (errno) {} // no-warning
return;
}
if (errno) {} // expected-warning{{An undefined value may be read from 'errno' [alpha.unix.Errno]}}
}
void check_tmpfile(void) {
FILE *F = tmpfile();
if (!F) {
clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
if (errno) {} // no-warning
return;
}
if (errno) {} // expected-warning{{An undefined value may be read from 'errno' [alpha.unix.Errno]}}
}
void check_freopen(void) {
FILE *F = tmpfile();
if (!F)
return;
F = freopen("xxx", "w", F);
if (!F) {
clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
if (errno) {} // no-warning
return;
}
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
}
void check_fclose(void) {
FILE *F = tmpfile();
if (!F)
return;
int Ret = fclose(F);
if (Ret == EOF) {
clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
if (errno) {} // no-warning
return;
}
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
}
void check_fread_size0(void) {
char Buf[10];
FILE *F = tmpfile();
if (!F)
return;
fread(Buf, 0, 1, F);
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
}
void check_fread_nmemb0(void) {
char Buf[10];
FILE *F = tmpfile();
if (!F)
return;
fread(Buf, 1, 0, F);
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
}
void check_fread(void) {
char Buf[10];
FILE *F = tmpfile();
if (!F)
return;
int R = fread(Buf, 1, 10, F);
if (R < 10) {
clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
if (errno) {} // no-warning
fclose(F);
return;
}
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
}
void check_fwrite_size0(void) {
char Buf[] = "0123456789";
FILE *F = tmpfile();
if (!F)
return;
fwrite(Buf, 0, 1, F);
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
}
void check_fwrite_nmemb0(void) {
char Buf[] = "0123456789";
FILE *F = tmpfile();
if (!F)
return;
fwrite(Buf, 1, 0, F);
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
}
void check_fwrite(void) {
char Buf[] = "0123456789";
FILE *F = tmpfile();
if (!F)
return;
int R = fwrite(Buf, 1, 10, F);
if (R < 10) {
clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
if (errno) {} // no-warning
fclose(F);
return;
}
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
}
void check_fseek(void) {
FILE *F = tmpfile();
if (!F)
return;
int S = fseek(F, 11, SEEK_SET);
if (S != 0) {
clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
if (errno) {} // no-warning
fclose(F);
return;
}
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
}
void check_no_errno_change(void) {
FILE *F = tmpfile();
if (!F)
return;
errno = 1;
clearerr(F);
if (errno) {} // no-warning
feof(F);
if (errno) {} // no-warning
ferror(F);
if (errno) {} // no-warning
clang_analyzer_eval(errno == 1); // expected-warning{{TRUE}}
fclose(F);
}
void check_fgetpos(void) {
FILE *F = tmpfile();
if (!F)
return;
errno = 0;
fpos_t Pos;
int Ret = fgetpos(F, &Pos);
if (Ret)
clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
else
clang_analyzer_eval(errno == 0); // expected-warning{{TRUE}}
if (errno) {} // no-warning
fclose(F);
}
void check_fsetpos(void) {
FILE *F = tmpfile();
if (!F)
return;
errno = 0;
fpos_t Pos;
int Ret = fsetpos(F, &Pos);
if (Ret)
clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
else
clang_analyzer_eval(errno == 0); // expected-warning{{TRUE}}
if (errno) {} // no-warning
fclose(F);
}
void check_ftell(void) {
FILE *F = tmpfile();
if (!F)
return;
errno = 0;
long Ret = ftell(F);
if (Ret == -1) {
clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
} else {
clang_analyzer_eval(errno == 0); // expected-warning{{TRUE}}
clang_analyzer_eval(Ret >= 0); // expected-warning{{TRUE}}
}
if (errno) {} // no-warning
fclose(F);
}
void check_rewind(void) {
FILE *F = tmpfile();
if (!F)
return;
errno = 0;
rewind(F);
clang_analyzer_eval(errno == 0);
// expected-warning@-1{{FALSE}}
// expected-warning@-2{{TRUE}}
fclose(F);
}
void check_fileno(void) {
FILE *F = tmpfile();
if (!F)
return;
int N = fileno(F);
if (N == -1) {
clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
if (errno) {} // no-warning
fclose(F);
return;
}
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
}

View File

@ -7,6 +7,7 @@
#include "Inputs/system-header-simulator.h"
void clang_analyzer_eval(int);
void clang_analyzer_dump(int);
void clang_analyzer_warnIfReached(void);
void StreamTesterChecker_make_feof_stream(FILE *);
void StreamTesterChecker_make_ferror_stream(FILE *);
@ -101,10 +102,15 @@ void error_fwrite(void) {
}
void freadwrite_zerosize(FILE *F) {
fwrite(0, 1, 0, F);
fwrite(0, 0, 1, F);
fread(0, 1, 0, F);
fread(0, 0, 1, F);
size_t Ret;
Ret = fwrite(0, 1, 0, F);
clang_analyzer_dump(Ret); // expected-warning {{0 }}
Ret = fwrite(0, 0, 1, F);
clang_analyzer_dump(Ret); // expected-warning {{0 }}
Ret = fread(0, 1, 0, F);
clang_analyzer_dump(Ret); // expected-warning {{0 }}
Ret = fread(0, 0, 1, F);
clang_analyzer_dump(Ret); // expected-warning {{0 }}
}
void freadwrite_zerosize_eofstate(FILE *F) {

View File

@ -0,0 +1,157 @@
// RUN: %clang_analyze_cc1 -verify %s \
// RUN: -analyzer-checker=core \
// RUN: -analyzer-checker=alpha.unix.Errno \
// RUN: -analyzer-checker=alpha.unix.Stream \
// RUN: -analyzer-checker=apiModeling.StdCLibraryFunctions \
// RUN: -analyzer-config apiModeling.StdCLibraryFunctions:ModelPOSIX=true \
// RUN: -analyzer-checker=debug.ExprInspection
// enable only StdCLibraryFunctions checker
// RUN: %clang_analyze_cc1 -verify %s \
// RUN: -analyzer-checker=core \
// RUN: -analyzer-checker=alpha.unix.Errno \
// RUN: -analyzer-checker=apiModeling.StdCLibraryFunctions \
// RUN: -analyzer-config apiModeling.StdCLibraryFunctions:ModelPOSIX=true \
// RUN: -analyzer-checker=debug.ExprInspection
#include "Inputs/system-header-simulator.h"
#include "Inputs/errno_var.h"
void clang_analyzer_eval(int);
const char *WBuf = "123456789";
char RBuf[10];
void test_freopen(FILE *F) {
F = freopen("xxx", "w", F);
if (F) {
if (errno) {} // expected-warning{{undefined}}
} else {
clang_analyzer_eval(errno != 0); // expected-warning {{TRUE}}
}
}
void test_fread(FILE *F) {
size_t Ret = fread(RBuf, 1, 10, F);
if (Ret == 10) {
if (errno) {} // expected-warning{{undefined}}
} else {
clang_analyzer_eval(errno != 0); // expected-warning {{TRUE}}
}
clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
}
void test_fwrite(FILE *F) {
size_t Ret = fwrite(WBuf, 1, 10, F);
if (Ret == 10) {
if (errno) {} // expected-warning{{undefined}}
} else {
clang_analyzer_eval(errno != 0); // expected-warning {{TRUE}}
}
clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
}
void test_fclose(FILE *F) {
int Ret = fclose(F);
if (Ret == 0) {
if (errno) {} // expected-warning{{undefined}}
} else {
clang_analyzer_eval(Ret == EOF); // expected-warning {{TRUE}}
clang_analyzer_eval(errno != 0); // expected-warning {{TRUE}}
}
clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
}
void test_fseek(FILE *F) {
int Ret = fseek(F, SEEK_SET, 1);
if (Ret == 0) {
if (errno) {} // expected-warning{{undefined}}
} else {
clang_analyzer_eval(Ret == -1); // expected-warning {{TRUE}}
clang_analyzer_eval(errno != 0); // expected-warning {{TRUE}}
}
clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
}
void check_fgetpos(FILE *F) {
errno = 0;
fpos_t Pos;
int Ret = fgetpos(F, &Pos);
if (Ret)
clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
else
clang_analyzer_eval(errno == 0); // expected-warning{{TRUE}}
// expected-warning@-1{{FALSE}}
if (errno) {} // no-warning
clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
}
void check_fsetpos(FILE *F) {
errno = 0;
fpos_t Pos;
int Ret = fsetpos(F, &Pos);
if (Ret)
clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
else
clang_analyzer_eval(errno == 0); // expected-warning{{TRUE}}
// expected-warning@-1{{FALSE}}
if (errno) {} // no-warning
clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
}
void check_ftell(FILE *F) {
errno = 0;
long Ret = ftell(F);
if (Ret == -1) {
clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
} else {
clang_analyzer_eval(errno == 0); // expected-warning{{TRUE}}
// expected-warning@-1{{FALSE}}
clang_analyzer_eval(Ret >= 0); // expected-warning{{TRUE}}
}
if (errno) {} // no-warning
clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
}
void freadwrite_zerosize(FILE *F) {
fwrite(WBuf, 1, 0, F);
clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
fwrite(WBuf, 0, 1, F);
clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
fread(RBuf, 1, 0, F);
clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
fread(RBuf, 0, 1, F);
clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
}
void freadwrite_zerosize_errno(FILE *F, int A) {
switch (A) {
case 1:
fwrite(WBuf, 1, 0, F);
if (errno) {} // expected-warning{{undefined}}
break;
case 2:
fwrite(WBuf, 0, 1, F);
if (errno) {} // expected-warning{{undefined}}
break;
case 3:
fread(RBuf, 1, 0, F);
if (errno) {} // expected-warning{{undefined}}
break;
case 4:
fread(RBuf, 0, 1, F);
if (errno) {} // expected-warning{{undefined}}
break;
}
}