[analyzer] CStringChecker.cpp - Code refactoring on bug report.

Reviewers: NoQ, george.karpenkov, xazax.hun

Reviewed By: george.karpenkov	

Differential Revision: https://reviews.llvm.org/D44557

llvm-svn: 330589
This commit is contained in:
Henry Wong 2018-04-23 13:36:51 +00:00
parent 455d0b2cfe
commit 7af1c99024
1 changed files with 96 additions and 100 deletions

View File

@ -194,6 +194,14 @@ public:
const Stmt *First, const Stmt *First,
const Stmt *Second) const; const Stmt *Second) const;
void emitNullArgBug(CheckerContext &C, ProgramStateRef State, const Stmt *S,
StringRef WarningMsg) const;
void emitOutOfBoundsBug(CheckerContext &C, ProgramStateRef State,
const Stmt *S, StringRef WarningMsg) const;
void emitNotCStringBug(CheckerContext &C, ProgramStateRef State,
const Stmt *S, StringRef WarningMsg) const;
void emitAdditionOverflowBug(CheckerContext &C, ProgramStateRef State) const;
ProgramStateRef checkAdditionOverflow(CheckerContext &C, ProgramStateRef checkAdditionOverflow(CheckerContext &C,
ProgramStateRef state, ProgramStateRef state,
NonLoc left, NonLoc left,
@ -239,30 +247,14 @@ ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C,
std::tie(stateNull, stateNonNull) = assumeZero(C, state, l, S->getType()); std::tie(stateNull, stateNonNull) = assumeZero(C, state, l, S->getType());
if (stateNull && !stateNonNull) { if (stateNull && !stateNonNull) {
if (!Filter.CheckCStringNullArg) if (Filter.CheckCStringNullArg) {
return nullptr; SmallString<80> buf;
llvm::raw_svector_ostream os(buf);
assert(CurrentFunctionDescription);
os << "Null pointer argument in call to " << CurrentFunctionDescription;
ExplodedNode *N = C.generateErrorNode(stateNull); emitNullArgBug(C, stateNull, S, os.str());
if (!N) }
return nullptr;
if (!BT_Null)
BT_Null.reset(new BuiltinBug(
Filter.CheckNameCStringNullArg, categories::UnixAPI,
"Null pointer argument in call to byte string function"));
SmallString<80> buf;
llvm::raw_svector_ostream os(buf);
assert(CurrentFunctionDescription);
os << "Null pointer argument in call to " << CurrentFunctionDescription;
// Generate a report for this bug.
BuiltinBug *BT = static_cast<BuiltinBug*>(BT_Null.get());
auto report = llvm::make_unique<BugReport>(*BT, os.str(), N);
report->addRange(S->getSourceRange());
bugreporter::trackNullOrUndefValue(N, S, *report);
C.emitReport(std::move(report));
return nullptr; return nullptr;
} }
@ -305,31 +297,14 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
ProgramStateRef StInBound = state->assumeInBound(Idx, Size, true); ProgramStateRef StInBound = state->assumeInBound(Idx, Size, true);
ProgramStateRef StOutBound = state->assumeInBound(Idx, Size, false); ProgramStateRef StOutBound = state->assumeInBound(Idx, Size, false);
if (StOutBound && !StInBound) { if (StOutBound && !StInBound) {
ExplodedNode *N = C.generateErrorNode(StOutBound);
if (!N)
return nullptr;
CheckName Name;
// These checks are either enabled by the CString out-of-bounds checker // These checks are either enabled by the CString out-of-bounds checker
// explicitly or the "basic" CStringNullArg checker support that Malloc // explicitly or the "basic" CStringNullArg checker support that Malloc
// checker enables. // checker enables.
assert(Filter.CheckCStringOutOfBounds || Filter.CheckCStringNullArg); assert(Filter.CheckCStringOutOfBounds || Filter.CheckCStringNullArg);
if (Filter.CheckCStringOutOfBounds)
Name = Filter.CheckNameCStringOutOfBounds;
else
Name = Filter.CheckNameCStringNullArg;
if (!BT_Bounds) { // Emit a bug report.
BT_Bounds.reset(new BuiltinBug(
Name, "Out-of-bound array access",
"Byte string function accesses out-of-bound array element"));
}
BuiltinBug *BT = static_cast<BuiltinBug*>(BT_Bounds.get());
// Generate a report for this bug.
std::unique_ptr<BugReport> report;
if (warningMsg) { if (warningMsg) {
report = llvm::make_unique<BugReport>(*BT, warningMsg, N); emitOutOfBoundsBug(C, StOutBound, S, warningMsg);
} else { } else {
assert(CurrentFunctionDescription); assert(CurrentFunctionDescription);
assert(CurrentFunctionDescription[0] != '\0'); assert(CurrentFunctionDescription[0] != '\0');
@ -339,15 +314,8 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
os << toUppercase(CurrentFunctionDescription[0]) os << toUppercase(CurrentFunctionDescription[0])
<< &CurrentFunctionDescription[1] << &CurrentFunctionDescription[1]
<< " accesses out-of-bound array element"; << " accesses out-of-bound array element";
report = llvm::make_unique<BugReport>(*BT, os.str(), N); emitOutOfBoundsBug(C, StOutBound, S, os.str());
} }
// FIXME: It would be nice to eventually make this diagnostic more clear,
// e.g., by referencing the original declaration or by saying *why* this
// reference is outside the range.
report->addRange(S->getSourceRange());
C.emitReport(std::move(report));
return nullptr; return nullptr;
} }
@ -567,6 +535,79 @@ void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state,
C.emitReport(std::move(report)); C.emitReport(std::move(report));
} }
void CStringChecker::emitNullArgBug(CheckerContext &C, ProgramStateRef State,
const Stmt *S, StringRef WarningMsg) const {
if (ExplodedNode *N = C.generateErrorNode(State)) {
if (!BT_Null)
BT_Null.reset(new BuiltinBug(
Filter.CheckNameCStringNullArg, categories::UnixAPI,
"Null pointer argument in call to byte string function"));
BuiltinBug *BT = static_cast<BuiltinBug *>(BT_Null.get());
auto Report = llvm::make_unique<BugReport>(*BT, WarningMsg, N);
bugreporter::trackNullOrUndefValue(N, S, *Report);
C.emitReport(std::move(Report));
}
}
void CStringChecker::emitOutOfBoundsBug(CheckerContext &C,
ProgramStateRef State, const Stmt *S,
StringRef WarningMsg) const {
if (ExplodedNode *N = C.generateErrorNode(State)) {
if (!BT_Bounds)
BT_Bounds.reset(new BuiltinBug(
Filter.CheckCStringOutOfBounds ? Filter.CheckNameCStringOutOfBounds
: Filter.CheckNameCStringNullArg,
"Out-of-bound array access",
"Byte string function accesses out-of-bound array element"));
BuiltinBug *BT = static_cast<BuiltinBug *>(BT_Bounds.get());
// FIXME: It would be nice to eventually make this diagnostic more clear,
// e.g., by referencing the original declaration or by saying *why* this
// reference is outside the range.
auto Report = llvm::make_unique<BugReport>(*BT, WarningMsg, N);
Report->addRange(S->getSourceRange());
C.emitReport(std::move(Report));
}
}
void CStringChecker::emitNotCStringBug(CheckerContext &C, ProgramStateRef State,
const Stmt *S,
StringRef WarningMsg) const {
if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
if (!BT_NotCString)
BT_NotCString.reset(new BuiltinBug(
Filter.CheckNameCStringNotNullTerm, categories::UnixAPI,
"Argument is not a null-terminated string."));
auto Report = llvm::make_unique<BugReport>(*BT_NotCString, WarningMsg, N);
Report->addRange(S->getSourceRange());
C.emitReport(std::move(Report));
}
}
void CStringChecker::emitAdditionOverflowBug(CheckerContext &C,
ProgramStateRef State) const {
if (ExplodedNode *N = C.generateErrorNode(State)) {
if (!BT_NotCString)
BT_NotCString.reset(
new BuiltinBug(Filter.CheckNameCStringOutOfBounds, "API",
"Sum of expressions causes overflow."));
// This isn't a great error message, but this should never occur in real
// code anyway -- you'd have to create a buffer longer than a size_t can
// represent, which is sort of a contradiction.
const char *WarningMsg =
"This expression will create a string whose length is too big to "
"be represented as a size_t";
auto Report = llvm::make_unique<BugReport>(*BT_NotCString, WarningMsg, N);
C.emitReport(std::move(Report));
}
}
ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C, ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C,
ProgramStateRef state, ProgramStateRef state,
NonLoc left, NonLoc left,
@ -610,26 +651,7 @@ ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C,
if (stateOverflow && !stateOkay) { if (stateOverflow && !stateOkay) {
// We have an overflow. Emit a bug report. // We have an overflow. Emit a bug report.
ExplodedNode *N = C.generateErrorNode(stateOverflow); emitAdditionOverflowBug(C, stateOverflow);
if (!N)
return nullptr;
if (!BT_AdditionOverflow)
BT_AdditionOverflow.reset(
new BuiltinBug(Filter.CheckNameCStringOutOfBounds, "API",
"Sum of expressions causes overflow"));
// This isn't a great error message, but this should never occur in real
// code anyway -- you'd have to create a buffer longer than a size_t can
// represent, which is sort of a contradiction.
const char *warning =
"This expression will create a string whose length is too big to "
"be represented as a size_t";
// Generate a report for this bug.
C.emitReport(
llvm::make_unique<BugReport>(*BT_AdditionOverflow, warning, N));
return nullptr; return nullptr;
} }
@ -729,15 +751,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
// C string. In the context of locations, the only time we can issue such // C string. In the context of locations, the only time we can issue such
// a warning is for labels. // a warning is for labels.
if (Optional<loc::GotoLabel> Label = Buf.getAs<loc::GotoLabel>()) { if (Optional<loc::GotoLabel> Label = Buf.getAs<loc::GotoLabel>()) {
if (!Filter.CheckCStringNotNullTerm) if (Filter.CheckCStringNotNullTerm) {
return UndefinedVal();
if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) {
if (!BT_NotCString)
BT_NotCString.reset(new BuiltinBug(
Filter.CheckNameCStringNotNullTerm, categories::UnixAPI,
"Argument is not a null-terminated string."));
SmallString<120> buf; SmallString<120> buf;
llvm::raw_svector_ostream os(buf); llvm::raw_svector_ostream os(buf);
assert(CurrentFunctionDescription); assert(CurrentFunctionDescription);
@ -745,14 +759,9 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
<< " is the address of the label '" << Label->getLabel()->getName() << " is the address of the label '" << Label->getLabel()->getName()
<< "', which is not a null-terminated string"; << "', which is not a null-terminated string";
// Generate a report for this bug. emitNotCStringBug(C, state, Ex, os.str());
auto report = llvm::make_unique<BugReport>(*BT_NotCString, os.str(), N);
report->addRange(Ex->getSourceRange());
C.emitReport(std::move(report));
} }
return UndefinedVal(); return UndefinedVal();
} }
// If it's not a region and not a label, give up. // If it's not a region and not a label, give up.
@ -789,15 +798,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
// Other regions (mostly non-data) can't have a reliable C string length. // Other regions (mostly non-data) can't have a reliable C string length.
// In this case, an error is emitted and UndefinedVal is returned. // In this case, an error is emitted and UndefinedVal is returned.
// The caller should always be prepared to handle this case. // The caller should always be prepared to handle this case.
if (!Filter.CheckCStringNotNullTerm) if (Filter.CheckCStringNotNullTerm) {
return UndefinedVal();
if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) {
if (!BT_NotCString)
BT_NotCString.reset(new BuiltinBug(
Filter.CheckNameCStringNotNullTerm, categories::UnixAPI,
"Argument is not a null-terminated string."));
SmallString<120> buf; SmallString<120> buf;
llvm::raw_svector_ostream os(buf); llvm::raw_svector_ostream os(buf);
@ -809,13 +810,8 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
else else
os << "not a null-terminated string"; os << "not a null-terminated string";
// Generate a report for this bug. emitNotCStringBug(C, state, Ex, os.str());
auto report = llvm::make_unique<BugReport>(*BT_NotCString, os.str(), N);
report->addRange(Ex->getSourceRange());
C.emitReport(std::move(report));
} }
return UndefinedVal(); return UndefinedVal();
} }
} }