Diff
checker
텍스트
텍스트
이미지
문서
Excel
폴더
Legal
Enterprise
데스크톱
요금제
로그인
데스크톱 앱 다운로드
텍스트 비교
두 텍스트 파일의 차이점을 찾아보세요
도구
기록
실시간 편집
변경 없는 행 숨기기
줄바꿈 비활성화
레이아웃
나란히 보기
합쳐 보기
비교 단위
스마트
단어
글자
구문 강조
언어 선택
제외
텍스트 변환
첫 변경으로
수정
Diffchecker Desktop
가장 안전하게 Diffchecker를 사용하는 방법. 데스크톱 앱을 사용하면 비교 데이터가 외부로 전송되지 않습니다!
데스크톱 앱 받기
Untitled diff
생성일
10년 전
비교 결과 만료 없음
초기화
내보내기
공유
설명
156 삭제
행
총
삭제
글자
총
삭제
이 기능을 계속 사용하려면 업그레이드해 주세요
Diff
checker
Pro
요금제 보기
271 행
복사
105 추가
행
총
추가
글자
총
추가
이 기능을 계속 사용하려면 업그레이드해 주세요
Diff
checker
Pro
요금제 보기
223 행
복사
//===-- SimpleStreamChecker.cpp -----------------------------------------*- C++ -*--//
//===-- SimpleStreamChecker.cpp -----------------------------------------*- C++ -*--//
//
//
// The LLVM Compiler Infrastructure
// The LLVM Compiler Infrastructure
//
//
// This file is distributed under the University of Illinois Open Source
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// License. See LICENSE.TXT for details.
//
//
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
//
//
// Defines a checker for proper use of fopen/fclose APIs.
// Defines a checker for proper use of fopen/fclose APIs.
// - If a file has been closed with fclose, it should not be accessed again.
// - If a file has been closed with fclose, it should not be accessed again.
// Accessing a closed file results in undefined behavior.
// Accessing a closed file results in undefined behavior.
// - If a file was opened with fopen, it must be closed with fclose before
// - If a file was opened with fopen, it must be closed with fclose before
// the execution ends. Failing to do so results in a resource leak.
// the execution ends. Failing to do so results in a resource leak.
//
//
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
using namespace clang;
using namespace clang;
using namespace ento;
using namespace ento;
namespace {
namespace {
복사
복사됨
복사
복사됨
typedef SmallVector<
SymbolRef
, 2>
SymbolVector
;
enum SimpleStreamState { Closed, Opened };
SmartStateTrait SimpleStreamTrait;
typedef SmallVector<
const MemRegion *
, 2>
RegionVector
;
복사
복사됨
복사
복사됨
struct StreamState {
class SimpleStreamModel
private:
: public Checker<check::ASTDecl<TranslationUnitDecl>, check::PostCall> {
enum Kind { Opened, Closed } K;
CallDescription OpenFn, CloseFn;
StreamState(Kind InK) : K(InK) { }
bool guaranteedNotToCloseFile(const CallEvent &Call) const;
public:
public:
복사
복사됨
복사
복사됨
bool isOpened
()
const { return K ==
Open
ed; }
SimpleStreamModel
()
:
Open
Fn("fopen"),
Close
Fn("fclose") {
}
bool is
Close
d() const { return K == Closed;
}
복사
복사됨
복사
복사됨
static StreamState getOpened() { return StreamState(Opened); }
void checkASTDecl(const TranslationUnitDecl *D, AnalysisManager &AMgr,
static StreamState getClosed() { return StreamState(Closed); }
BugReporter &BR) const;
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
ProgramStateRef checkPointerEscape(ProgramStateRef State,
const InvalidatedSymbols &Escaped,
const CallEvent *Call,
PointerEscapeKind Kind) const;
};
} // end of anonymous namespace
복사
복사됨
복사
복사됨
bool operator==(const StreamState &X
) const {
void SimpleStreamModel::checkASTDecl(const TranslationUnitDecl *D,
return K == X.K;
AnalysisManager &AMgr,
}
BugReporter &BR) const {
void Profile(llvm::FoldingSetNodeID &ID) const {
// Once the AST context is available, we can express the fact that our trait
ID.AddInteger(K);
// has type 'int'.
// FIXME: This is ugly. Maybe make a separate callback, or delay registering
// checkers until AST is constructed?
SimpleStreamTrait.initialize("SimpleStream", AMgr.getASTContext().IntTy);
}
Text moved with changes from lines 117-122 (98.1% similarity)
void SimpleStreamModel::checkPostCall(const CallEvent &Call,
CheckerContext &C
) const {
if (!Call.isGlobalCFunction())
return;
if (Call.isCalled(OpenFn)) {
// Get the symbolic value corresponding to the file handle.
const MemRegion *FileDesc = Call.getReturnValue().getAsRegion();
if (!FileDesc)
return;
// Generate the next transition (an edge in the exploded graph).
ProgramStateRef State = C.getState();
State = State->bindLoc(SimpleStreamTrait, FileDesc, Opened);
C.addTransition(State);
} else if (Call.isCalled(CloseFn)) {
// Get the symbolic value corresponding to the file handle.
const MemRegion *FileDesc = Call.getArgSVal(0).getAsRegion();
if (!FileDesc)
return;
// Generate the next transition, in which the stream is closed.
ProgramStateRef State = C.getState();
State = State->bindLoc(SimpleStreamTrait, FileDesc, Closed);
C.addTransition(State);
}
}
복사
복사됨
복사
복사됨
}
;
}
복사
복사됨
복사
복사됨
class SimpleStreamChecker
: public Checker<check:
:PostCall,
namespace {
check:
:PreCall,
class SimpleStreamChecker
check::DeadSymbols
,
: public Checker<check:
:PreCall,
check::DeadSymbols
> {
check::PointerEscape
> {
CallDescription
CloseFn;
CallDescription
OpenFn,
CloseFn;
std::unique_ptr<BugType> DoubleCloseBugType;
std::unique_ptr<BugType> DoubleCloseBugType;
std::unique_ptr<BugType> LeakBugType;
std::unique_ptr<BugType> LeakBugType;
복사
복사됨
복사
복사됨
void reportDoubleClose(
SymbolRef
FileDesc
Sym
,
void reportDoubleClose(
const MemRegion *
FileDesc
Reg
,
const CallEvent &Call,
const CallEvent &Call,
CheckerContext &C) const;
CheckerContext &C) const;
복사
복사됨
복사
복사됨
void reportLeaks(ArrayRef<
SymbolRef
> LeakedStreams, CheckerContext &C,
void reportLeaks(ArrayRef<
const MemRegion *
> LeakedStreams, CheckerContext &C,
ExplodedNode *ErrNode) const;
ExplodedNode *ErrNode) const;
복사
복사됨
복사
복사됨
bool guaranteedNotToCloseFile(const CallEvent &Call) const;
public:
public:
SimpleStreamChecker();
SimpleStreamChecker();
복사
복사됨
복사
복사됨
/// Process fopen.
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
/// Process fclose.
/// Process fclose.
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
복사
복사됨
복사
복사됨
/// Detect leaks.
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
복사
복사됨
복사
복사됨
/// Stop tracking addresses which escape.
ProgramStateRef checkPointerEscape(ProgramStateRef State,
const InvalidatedSymbols &Escaped,
const CallEvent *Call,
PointerEscapeKind Kind) const;
};
} // end anonymous namespace
/// The state of the checker is a map from tracked stream symbols to their
/// state. Let's store it in the ProgramState.
REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
namespace {
class StopTrackingCallback final : public SymbolVisitor {
ProgramStateRef state;
public:
StopTrackingCallback(ProgramStateRef st) : state(st) {}
ProgramStateRef getState() const { return state; }
bool VisitSymbol(SymbolRef sym) override {
state = state->remove<StreamMap>(sym);
return true;
}
};
};
} // end anonymous namespace
} // end anonymous namespace
복사
복사됨
복사
복사됨
SimpleStreamChecker::SimpleStreamChecker()
SimpleStreamChecker::SimpleStreamChecker()
:
CloseFn("fclose", 1) {
: OpenFn("fopen"),
CloseFn("fclose", 1) {
// Initialize the bug types.
// Initialize the bug types.
DoubleCloseBugType.reset(
DoubleCloseBugType.reset(
new BugType(this, "Double fclose", "Unix Stream API Error"));
new BugType(this, "Double fclose", "Unix Stream API Error"));
LeakBugType.reset(
LeakBugType.reset(
new BugType(this, "Resource Leak", "Unix Stream API Error"));
new BugType(this, "Resource Leak", "Unix Stream API Error"));
// Sinks are higher importance bugs as well as calls to assert() or exit(0).
// Sinks are higher importance bugs as well as calls to assert() or exit(0).
LeakBugType->setSuppressOnSink(true);
LeakBugType->setSuppressOnSink(true);
}
}
복사
복사됨
복사
복사됨
Text moved with changes to lines 60-65 (98.1% similarity)
void SimpleStreamChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
if (!Call.isGlobalCFunction())
return;
if (!Call.isCalled(OpenFn))
return;
// Get the symbolic value corresponding to the file handle.
SymbolRef FileDesc = Call.getReturnValue().getAsSymbol();
if (!FileDesc)
return;
// Generate the next transition (an edge in the exploded graph).
ProgramStateRef State = C.getState();
State = State->set<StreamMap>(FileDesc, StreamState::getOpened());
C.addTransition(State);
}
void SimpleStreamChecker::checkPreCall(const CallEvent &Call,
void SimpleStreamChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
CheckerContext &C) const {
if (!Call.isGlobalCFunction())
if (!Call.isGlobalCFunction())
return;
return;
if (!Call.isCalled(CloseFn))
if (!Call.isCalled(CloseFn))
return;
return;
// Get the symbolic value corresponding to the file handle.
// Get the symbolic value corresponding to the file handle.
복사
복사됨
복사
복사됨
SymbolRef
FileDesc = Call.getArgSVal(0).
getAsSymbol
();
const MemRegion *
FileDesc = Call.getArgSVal(0).
getAsRegion
();
if (!FileDesc)
if (!FileDesc)
return;
return;
복사
복사됨
복사
복사됨
// Check if the stream has already been closed.
// Check if the stream has already been closed.
ProgramStateRef State = C.getState();
ProgramStateRef State = C.getState();
복사
복사됨
복사
복사됨
const StreamState *SS
= State->
get<StreamMap>(
FileDesc);
SVal Val
= State->
getSVal(SimpleStreamTrait,
FileDesc);
if (
SS && SS->isClosed(
)) {
if (
Val.isConstant(Closed
)) {
reportDoubleClose(FileDesc, Call, C);
reportDoubleClose(FileDesc, Call, C);
return;
return;
}
}
복사
복사됨
복사
복사됨
// Generate the next transition, in which the stream is closed.
State = State->set<StreamMap>(FileDesc, StreamState::getClosed());
C.addTransition(State);
}
}
복사
복사됨
복사
복사됨
static bool isLeaked(
SymbolRef Sym,
const
StreamState &SS,
static bool isLeaked(
const
MemRegion *Reg
, ProgramStateRef State) {
bool IsSymDead
, ProgramStateRef State) {
// If a symbol is NULL, assume that fopen failed on this path.
if (IsSymDead && SS.isOpened()) {
// A symbol should only be considered leaked if it is non-null.
// If a symbol is NULL, assume that fopen failed on this path.
if (const SymbolicRegion *SR = Reg->getSymbolicBase()) {
// A symbol should only be considered leaked if it is non-null.
SymbolRef Sym = SR->getSymbol();
ConstraintManager &CMgr = State->getConstraintManager();
ConstraintManager &CMgr = State->getConstraintManager();
ConditionTruthVal OpenFailed = CMgr.isNull(State, Sym);
ConditionTruthVal OpenFailed = CMgr.isNull(State, Sym);
return !OpenFailed.isConstrainedTrue();
return !OpenFailed.isConstrainedTrue();
}
}
return false;
return false;
}
}
void SimpleStreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
void SimpleStreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
CheckerContext &C) const {
CheckerContext &C) const {
ProgramStateRef State = C.getState();
ProgramStateRef State = C.getState();
복사
복사됨
복사
복사됨
SymbolVector
LeakedStreams;
RegionVector
LeakedStreams;
StreamMapTy TrackedStreams = State->get<StreamMap>();
// FIXME: Iterate through trait, not through dead symbols (might be faster)?
for (
StreamMapTy::iterator
I =
TrackedStreams.
begin(),
for (
auto
I =
SymReaper.dead_
begin(),
E =
SymReaper.dead_
end(); I != E; ++I) {
E =
TrackedStreams.
end(); I != E; ++I) {
// FIXME: This gets uglier with ghost fields.
SymbolRef Sym
=
I->first
;
const MemRegion *FileDesc
=
C.getSValBuilder().makeLoc(*I).getAsRegion()
;
bool IsSymDead = SymReaper.isDead(Sym);
SVal Val = State->getSVal(SimpleStreamTrait, FileDesc);
if (!Val.isConstant(Opened))
continue;
// Collect leaked symbols.
// Collect leaked symbols.
복사
복사됨
복사
복사됨
if (isLeaked(
Sym, I->second, IsSymDead
, State))
if (isLeaked(
FileDesc
, State))
LeakedStreams.push_back(
Sym);
LeakedStreams.push_back(
FileDesc);
// Remove the dead symbol from the streams map.
if (IsSymDead)
State = State->remove<StreamMap>(Sym);
}
}
복사
복사됨
복사
복사됨
if (LeakedStreams.empty())
return;
ExplodedNode *N = C.generateNonFatalErrorNode(State);
ExplodedNode *N = C.generateNonFatalErrorNode(State);
if (!N)
if (!N)
return;
return;
reportLeaks(LeakedStreams, C, N);
reportLeaks(LeakedStreams, C, N);
}
}
복사
복사됨
복사
복사됨
void SimpleStreamChecker::reportDoubleClose(
SymbolRef
FileDesc
Sym
,
void SimpleStreamChecker::reportDoubleClose(
const MemRegion *
FileDesc
Reg
,
const CallEvent &Call,
const CallEvent &Call,
CheckerContext &C) const {
CheckerContext &C) const {
// We reached a bug, stop exploring the path here by generating a sink.
// We reached a bug, stop exploring the path here by generating a sink.
ExplodedNode *ErrNode = C.generateErrorNode();
ExplodedNode *ErrNode = C.generateErrorNode();
// If we've already reached this node on another path, return.
// If we've already reached this node on another path, return.
if (!ErrNode)
if (!ErrNode)
return;
return;
// Generate the report.
// Generate the report.
auto R = llvm::make_unique<BugReport>(*DoubleCloseBugType,
auto R = llvm::make_unique<BugReport>(*DoubleCloseBugType,
"Closing a previously closed file stream", ErrNode);
"Closing a previously closed file stream", ErrNode);
R->addRange(Call.getSourceRange());
R->addRange(Call.getSourceRange());
복사
복사됨
복사
복사됨
R->markInteresting(FileDescSym);
C.emitReport(std::move(R));
C.emitReport(std::move(R));
}
}
복사
복사됨
복사
복사됨
void SimpleStreamChecker::reportLeaks(ArrayRef<
SymbolRef
> LeakedStreams,
void SimpleStreamChecker::reportLeaks(ArrayRef<
const MemRegion *
> LeakedStreams,
CheckerContext &C,
CheckerContext &C,
ExplodedNode *ErrNode) const {
ExplodedNode *ErrNode) const {
// Attach bug reports to the leak node.
// Attach bug reports to the leak node.
// TODO: Identify the leaked file descriptor.
// TODO: Identify the leaked file descriptor.
복사
복사됨
복사
복사됨
for (
SymbolRef
LeakedStream : LeakedStreams) {
for (
const MemRegion *
LeakedStream : LeakedStreams) {
auto R = llvm::make_unique<BugReport>(*LeakBugType,
auto R = llvm::make_unique<BugReport>(*LeakBugType,
"Opened file is never closed; potential resource leak", ErrNode);
"Opened file is never closed; potential resource leak", ErrNode);
복사
복사됨
복사
복사됨
R->markInteresting(
LeakedStream
);
if (const SymbolicRegion *SR = LeakedStream->getSymbolicBase())
R->markInteresting(
SR->getSymbol()
);
C.emitReport(std::move(R));
C.emitReport(std::move(R));
}
}
}
}
복사
복사됨
복사
복사됨
bool SimpleStreamChecker::guaranteedNotToCloseFile(const CallEvent &Call) const{
void ento::registerSimpleStreamChecker
V3
(CheckerManager &mgr) {
// If it's not in a system header, assume it might close a file.
mgr.registerChecker<SimpleStreamModel>();
if (!Call.isInSystemHeader())
return false;
// Handle cases where we know a buffer's /address/ can escape.
if (Call.argumentsMayEscape())
return false;
// Note, even though fclose closes the file, we do not list it here
// since the checker is modeling the call.
return true;
}
// If the pointer we are tracking escaped, do not track the symbol as
// we cannot reason about it anymore.
ProgramStateRef
SimpleStreamChecker::checkPointerEscape(ProgramStateRef State,
const InvalidatedSymbols &Escaped,
const CallEvent *Call,
PointerEscapeKind Kind) const {
// If we know that the call cannot close a file, there is nothing to do.
if (Kind == PSK_DirectEscapeOnCall && guaranteedNotToCloseFile(*Call)) {
return State;
}
for (InvalidatedSymbols::const_iterator I = Escaped.begin(),
E = Escaped.end();
I != E; ++I) {
SymbolRef Sym = *I;
// The symbol escaped. Optimistically, assume that the corresponding file
// handle will be closed somewhere else.
State = State->remove<StreamMap>(Sym);
}
return State;
}
void ento::registerSimpleStreamChecker
(CheckerManager &mgr) {
mgr.registerChecker<SimpleStreamChecker>();
mgr.registerChecker<SimpleStreamChecker>();
}
}
저장된 비교 결과
원본
파일 열기
//===-- SimpleStreamChecker.cpp -----------------------------------------*- C++ -*--// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Defines a checker for proper use of fopen/fclose APIs. // - If a file has been closed with fclose, it should not be accessed again. // Accessing a closed file results in undefined behavior. // - If a file was opened with fopen, it must be closed with fclose before // the execution ends. Failing to do so results in a resource leak. // //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" using namespace clang; using namespace ento; namespace { typedef SmallVector<SymbolRef, 2> SymbolVector; struct StreamState { private: enum Kind { Opened, Closed } K; StreamState(Kind InK) : K(InK) { } public: bool isOpened() const { return K == Opened; } bool isClosed() const { return K == Closed; } static StreamState getOpened() { return StreamState(Opened); } static StreamState getClosed() { return StreamState(Closed); } bool operator==(const StreamState &X) const { return K == X.K; } void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); } }; class SimpleStreamChecker : public Checker<check::PostCall, check::PreCall, check::DeadSymbols, check::PointerEscape> { CallDescription OpenFn, CloseFn; std::unique_ptr<BugType> DoubleCloseBugType; std::unique_ptr<BugType> LeakBugType; void reportDoubleClose(SymbolRef FileDescSym, const CallEvent &Call, CheckerContext &C) const; void reportLeaks(ArrayRef<SymbolRef> LeakedStreams, CheckerContext &C, ExplodedNode *ErrNode) const; bool guaranteedNotToCloseFile(const CallEvent &Call) const; public: SimpleStreamChecker(); /// Process fopen. void checkPostCall(const CallEvent &Call, CheckerContext &C) const; /// Process fclose. void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; /// Stop tracking addresses which escape. ProgramStateRef checkPointerEscape(ProgramStateRef State, const InvalidatedSymbols &Escaped, const CallEvent *Call, PointerEscapeKind Kind) const; }; } // end anonymous namespace /// The state of the checker is a map from tracked stream symbols to their /// state. Let's store it in the ProgramState. REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) namespace { class StopTrackingCallback final : public SymbolVisitor { ProgramStateRef state; public: StopTrackingCallback(ProgramStateRef st) : state(st) {} ProgramStateRef getState() const { return state; } bool VisitSymbol(SymbolRef sym) override { state = state->remove<StreamMap>(sym); return true; } }; } // end anonymous namespace SimpleStreamChecker::SimpleStreamChecker() : OpenFn("fopen"), CloseFn("fclose", 1) { // Initialize the bug types. DoubleCloseBugType.reset( new BugType(this, "Double fclose", "Unix Stream API Error")); LeakBugType.reset( new BugType(this, "Resource Leak", "Unix Stream API Error")); // Sinks are higher importance bugs as well as calls to assert() or exit(0). LeakBugType->setSuppressOnSink(true); } void SimpleStreamChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { if (!Call.isGlobalCFunction()) return; if (!Call.isCalled(OpenFn)) return; // Get the symbolic value corresponding to the file handle. SymbolRef FileDesc = Call.getReturnValue().getAsSymbol(); if (!FileDesc) return; // Generate the next transition (an edge in the exploded graph). ProgramStateRef State = C.getState(); State = State->set<StreamMap>(FileDesc, StreamState::getOpened()); C.addTransition(State); } void SimpleStreamChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (!Call.isGlobalCFunction()) return; if (!Call.isCalled(CloseFn)) return; // Get the symbolic value corresponding to the file handle. SymbolRef FileDesc = Call.getArgSVal(0).getAsSymbol(); if (!FileDesc) return; // Check if the stream has already been closed. ProgramStateRef State = C.getState(); const StreamState *SS = State->get<StreamMap>(FileDesc); if (SS && SS->isClosed()) { reportDoubleClose(FileDesc, Call, C); return; } // Generate the next transition, in which the stream is closed. State = State->set<StreamMap>(FileDesc, StreamState::getClosed()); C.addTransition(State); } static bool isLeaked(SymbolRef Sym, const StreamState &SS, bool IsSymDead, ProgramStateRef State) { if (IsSymDead && SS.isOpened()) { // If a symbol is NULL, assume that fopen failed on this path. // A symbol should only be considered leaked if it is non-null. ConstraintManager &CMgr = State->getConstraintManager(); ConditionTruthVal OpenFailed = CMgr.isNull(State, Sym); return !OpenFailed.isConstrainedTrue(); } return false; } void SimpleStreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const { ProgramStateRef State = C.getState(); SymbolVector LeakedStreams; StreamMapTy TrackedStreams = State->get<StreamMap>(); for (StreamMapTy::iterator I = TrackedStreams.begin(), E = TrackedStreams.end(); I != E; ++I) { SymbolRef Sym = I->first; bool IsSymDead = SymReaper.isDead(Sym); // Collect leaked symbols. if (isLeaked(Sym, I->second, IsSymDead, State)) LeakedStreams.push_back(Sym); // Remove the dead symbol from the streams map. if (IsSymDead) State = State->remove<StreamMap>(Sym); } ExplodedNode *N = C.generateNonFatalErrorNode(State); if (!N) return; reportLeaks(LeakedStreams, C, N); } void SimpleStreamChecker::reportDoubleClose(SymbolRef FileDescSym, const CallEvent &Call, CheckerContext &C) const { // We reached a bug, stop exploring the path here by generating a sink. ExplodedNode *ErrNode = C.generateErrorNode(); // If we've already reached this node on another path, return. if (!ErrNode) return; // Generate the report. auto R = llvm::make_unique<BugReport>(*DoubleCloseBugType, "Closing a previously closed file stream", ErrNode); R->addRange(Call.getSourceRange()); R->markInteresting(FileDescSym); C.emitReport(std::move(R)); } void SimpleStreamChecker::reportLeaks(ArrayRef<SymbolRef> LeakedStreams, CheckerContext &C, ExplodedNode *ErrNode) const { // Attach bug reports to the leak node. // TODO: Identify the leaked file descriptor. for (SymbolRef LeakedStream : LeakedStreams) { auto R = llvm::make_unique<BugReport>(*LeakBugType, "Opened file is never closed; potential resource leak", ErrNode); R->markInteresting(LeakedStream); C.emitReport(std::move(R)); } } bool SimpleStreamChecker::guaranteedNotToCloseFile(const CallEvent &Call) const{ // If it's not in a system header, assume it might close a file. if (!Call.isInSystemHeader()) return false; // Handle cases where we know a buffer's /address/ can escape. if (Call.argumentsMayEscape()) return false; // Note, even though fclose closes the file, we do not list it here // since the checker is modeling the call. return true; } // If the pointer we are tracking escaped, do not track the symbol as // we cannot reason about it anymore. ProgramStateRef SimpleStreamChecker::checkPointerEscape(ProgramStateRef State, const InvalidatedSymbols &Escaped, const CallEvent *Call, PointerEscapeKind Kind) const { // If we know that the call cannot close a file, there is nothing to do. if (Kind == PSK_DirectEscapeOnCall && guaranteedNotToCloseFile(*Call)) { return State; } for (InvalidatedSymbols::const_iterator I = Escaped.begin(), E = Escaped.end(); I != E; ++I) { SymbolRef Sym = *I; // The symbol escaped. Optimistically, assume that the corresponding file // handle will be closed somewhere else. State = State->remove<StreamMap>(Sym); } return State; } void ento::registerSimpleStreamChecker(CheckerManager &mgr) { mgr.registerChecker<SimpleStreamChecker>(); }
수정본
파일 열기
//===-- SimpleStreamChecker.cpp -----------------------------------------*- C++ -*--// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Defines a checker for proper use of fopen/fclose APIs. // - If a file has been closed with fclose, it should not be accessed again. // Accessing a closed file results in undefined behavior. // - If a file was opened with fopen, it must be closed with fclose before // the execution ends. Failing to do so results in a resource leak. // //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" using namespace clang; using namespace ento; namespace { enum SimpleStreamState { Closed, Opened }; SmartStateTrait SimpleStreamTrait; typedef SmallVector<const MemRegion *, 2> RegionVector; class SimpleStreamModel : public Checker<check::ASTDecl<TranslationUnitDecl>, check::PostCall> { CallDescription OpenFn, CloseFn; bool guaranteedNotToCloseFile(const CallEvent &Call) const; public: SimpleStreamModel() : OpenFn("fopen"), CloseFn("fclose") {} void checkASTDecl(const TranslationUnitDecl *D, AnalysisManager &AMgr, BugReporter &BR) const; void checkPostCall(const CallEvent &Call, CheckerContext &C) const; ProgramStateRef checkPointerEscape(ProgramStateRef State, const InvalidatedSymbols &Escaped, const CallEvent *Call, PointerEscapeKind Kind) const; }; } // end of anonymous namespace void SimpleStreamModel::checkASTDecl(const TranslationUnitDecl *D, AnalysisManager &AMgr, BugReporter &BR) const { // Once the AST context is available, we can express the fact that our trait // has type 'int'. // FIXME: This is ugly. Maybe make a separate callback, or delay registering // checkers until AST is constructed? SimpleStreamTrait.initialize("SimpleStream", AMgr.getASTContext().IntTy); } void SimpleStreamModel::checkPostCall(const CallEvent &Call, CheckerContext &C) const { if (!Call.isGlobalCFunction()) return; if (Call.isCalled(OpenFn)) { // Get the symbolic value corresponding to the file handle. const MemRegion *FileDesc = Call.getReturnValue().getAsRegion(); if (!FileDesc) return; // Generate the next transition (an edge in the exploded graph). ProgramStateRef State = C.getState(); State = State->bindLoc(SimpleStreamTrait, FileDesc, Opened); C.addTransition(State); } else if (Call.isCalled(CloseFn)) { // Get the symbolic value corresponding to the file handle. const MemRegion *FileDesc = Call.getArgSVal(0).getAsRegion(); if (!FileDesc) return; // Generate the next transition, in which the stream is closed. ProgramStateRef State = C.getState(); State = State->bindLoc(SimpleStreamTrait, FileDesc, Closed); C.addTransition(State); } } namespace { class SimpleStreamChecker : public Checker<check::PreCall, check::DeadSymbols> { CallDescription CloseFn; std::unique_ptr<BugType> DoubleCloseBugType; std::unique_ptr<BugType> LeakBugType; void reportDoubleClose(const MemRegion *FileDescReg, const CallEvent &Call, CheckerContext &C) const; void reportLeaks(ArrayRef<const MemRegion *> LeakedStreams, CheckerContext &C, ExplodedNode *ErrNode) const; public: SimpleStreamChecker(); /// Process fclose. void checkPreCall(const CallEvent &Call, CheckerContext &C) const; /// Detect leaks. void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; }; } // end anonymous namespace SimpleStreamChecker::SimpleStreamChecker() : CloseFn("fclose", 1) { // Initialize the bug types. DoubleCloseBugType.reset( new BugType(this, "Double fclose", "Unix Stream API Error")); LeakBugType.reset( new BugType(this, "Resource Leak", "Unix Stream API Error")); // Sinks are higher importance bugs as well as calls to assert() or exit(0). LeakBugType->setSuppressOnSink(true); } void SimpleStreamChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (!Call.isGlobalCFunction()) return; if (!Call.isCalled(CloseFn)) return; // Get the symbolic value corresponding to the file handle. const MemRegion *FileDesc = Call.getArgSVal(0).getAsRegion(); if (!FileDesc) return; // Check if the stream has already been closed. ProgramStateRef State = C.getState(); SVal Val = State->getSVal(SimpleStreamTrait, FileDesc); if (Val.isConstant(Closed)) { reportDoubleClose(FileDesc, Call, C); return; } } static bool isLeaked(const MemRegion *Reg, ProgramStateRef State) { // If a symbol is NULL, assume that fopen failed on this path. // A symbol should only be considered leaked if it is non-null. if (const SymbolicRegion *SR = Reg->getSymbolicBase()) { SymbolRef Sym = SR->getSymbol(); ConstraintManager &CMgr = State->getConstraintManager(); ConditionTruthVal OpenFailed = CMgr.isNull(State, Sym); return !OpenFailed.isConstrainedTrue(); } return false; } void SimpleStreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const { ProgramStateRef State = C.getState(); RegionVector LeakedStreams; // FIXME: Iterate through trait, not through dead symbols (might be faster)? for (auto I = SymReaper.dead_begin(), E = SymReaper.dead_end(); I != E; ++I) { // FIXME: This gets uglier with ghost fields. const MemRegion *FileDesc = C.getSValBuilder().makeLoc(*I).getAsRegion(); SVal Val = State->getSVal(SimpleStreamTrait, FileDesc); if (!Val.isConstant(Opened)) continue; // Collect leaked symbols. if (isLeaked(FileDesc, State)) LeakedStreams.push_back(FileDesc); } if (LeakedStreams.empty()) return; ExplodedNode *N = C.generateNonFatalErrorNode(State); if (!N) return; reportLeaks(LeakedStreams, C, N); } void SimpleStreamChecker::reportDoubleClose(const MemRegion *FileDescReg, const CallEvent &Call, CheckerContext &C) const { // We reached a bug, stop exploring the path here by generating a sink. ExplodedNode *ErrNode = C.generateErrorNode(); // If we've already reached this node on another path, return. if (!ErrNode) return; // Generate the report. auto R = llvm::make_unique<BugReport>(*DoubleCloseBugType, "Closing a previously closed file stream", ErrNode); R->addRange(Call.getSourceRange()); C.emitReport(std::move(R)); } void SimpleStreamChecker::reportLeaks(ArrayRef<const MemRegion *> LeakedStreams, CheckerContext &C, ExplodedNode *ErrNode) const { // Attach bug reports to the leak node. // TODO: Identify the leaked file descriptor. for (const MemRegion *LeakedStream : LeakedStreams) { auto R = llvm::make_unique<BugReport>(*LeakBugType, "Opened file is never closed; potential resource leak", ErrNode); if (const SymbolicRegion *SR = LeakedStream->getSymbolicBase()) R->markInteresting(SR->getSymbol()); C.emitReport(std::move(R)); } } void ento::registerSimpleStreamCheckerV3(CheckerManager &mgr) { mgr.registerChecker<SimpleStreamModel>(); mgr.registerChecker<SimpleStreamChecker>(); }
비교하기