mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-02-26 13:26:22 +00:00
[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:
parent
5867241eac
commit
5cf85323a0
@ -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 {
|
||||
|
@ -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);
|
||||
|
128
clang/test/Analysis/stream-errno-note.c
Normal file
128
clang/test/Analysis/stream-errno-note.c
Normal 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);
|
||||
}
|
224
clang/test/Analysis/stream-errno.c
Normal file
224
clang/test/Analysis/stream-errno.c
Normal 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'}}
|
||||
}
|
@ -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) {
|
||||
|
157
clang/test/Analysis/stream-noopen.c
Normal file
157
clang/test/Analysis/stream-noopen.c
Normal 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;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user