#include "InterCheckerAPI.h"
#include "clang/Basic/CharInfo.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/raw_ostream.h"
#include <functional>
using namespace clang;
using namespace ento;
using namespace std::placeholders;
namespace {
struct AnyArgExpr {
AnyArgExpr(const Expr *Expression, unsigned ArgumentIndex)
: Expression{Expression}, ArgumentIndex{ArgumentIndex} {}
const Expr *Expression;
unsigned ArgumentIndex;
};
struct SourceArgExpr : AnyArgExpr {
using AnyArgExpr::AnyArgExpr; };
struct DestinationArgExpr : AnyArgExpr {
using AnyArgExpr::AnyArgExpr; };
struct SizeArgExpr : AnyArgExpr {
using AnyArgExpr::AnyArgExpr; };
using ErrorMessage = SmallString<128>;
enum class AccessKind { write, read };
static ErrorMessage createOutOfBoundErrorMsg(StringRef FunctionDescription,
AccessKind Access) {
ErrorMessage Message;
llvm::raw_svector_ostream Os(Message);
Os << toUppercase(FunctionDescription.front())
<< &FunctionDescription.data()[1];
if (Access == AccessKind::write) {
Os << " overflows the destination buffer";
} else { Os << " accesses out-of-bound array element";
}
return Message;
}
enum class ConcatFnKind { none = 0, strcat = 1, strlcat = 2 };
class CStringChecker : public Checker< eval::Call,
check::PreStmt<DeclStmt>,
check::LiveSymbols,
check::DeadSymbols,
check::RegionChanges
> {
mutable std::unique_ptr<BugType> BT_Null, BT_Bounds, BT_Overlap,
BT_NotCString, BT_AdditionOverflow, BT_UninitRead;
mutable const char *CurrentFunctionDescription;
public:
struct CStringChecksFilter {
bool CheckCStringNullArg = false;
bool CheckCStringOutOfBounds = false;
bool CheckCStringBufferOverlap = false;
bool CheckCStringNotNullTerm = false;
bool CheckCStringUninitializedRead = false;
CheckerNameRef CheckNameCStringNullArg;
CheckerNameRef CheckNameCStringOutOfBounds;
CheckerNameRef CheckNameCStringBufferOverlap;
CheckerNameRef CheckNameCStringNotNullTerm;
CheckerNameRef CheckNameCStringUninitializedRead;
};
CStringChecksFilter Filter;
static void *getTag() { static int tag; return &tag; }
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const;
void checkLiveSymbols(ProgramStateRef state, SymbolReaper &SR) const;
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
ProgramStateRef
checkRegionChanges(ProgramStateRef state,
const InvalidatedSymbols *,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
const LocationContext *LCtx,
const CallEvent *Call) const;
using FnCheck = std::function<void(const CStringChecker *, CheckerContext &,
const CallExpr *)>;
CallDescriptionMap<FnCheck> Callbacks = {
{{CDF_MaybeBuiltin, "memcpy", 3},
std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, false)},
{{CDF_MaybeBuiltin, "wmemcpy", 3},
std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, true)},
{{CDF_MaybeBuiltin, "mempcpy", 3}, &CStringChecker::evalMempcpy},
{{CDF_MaybeBuiltin, "memcmp", 3}, &CStringChecker::evalMemcmp},
{{CDF_MaybeBuiltin, "memmove", 3}, &CStringChecker::evalMemmove},
{{CDF_MaybeBuiltin, "memset", 3}, &CStringChecker::evalMemset},
{{CDF_MaybeBuiltin, "explicit_memset", 3}, &CStringChecker::evalMemset},
{{CDF_MaybeBuiltin, "strcpy", 2}, &CStringChecker::evalStrcpy},
{{CDF_MaybeBuiltin, "strncpy", 3}, &CStringChecker::evalStrncpy},
{{CDF_MaybeBuiltin, "stpcpy", 2}, &CStringChecker::evalStpcpy},
{{CDF_MaybeBuiltin, "strlcpy", 3}, &CStringChecker::evalStrlcpy},
{{CDF_MaybeBuiltin, "strcat", 2}, &CStringChecker::evalStrcat},
{{CDF_MaybeBuiltin, "strncat", 3}, &CStringChecker::evalStrncat},
{{CDF_MaybeBuiltin, "strlcat", 3}, &CStringChecker::evalStrlcat},
{{CDF_MaybeBuiltin, "strlen", 1}, &CStringChecker::evalstrLength},
{{CDF_MaybeBuiltin, "wcslen", 1}, &CStringChecker::evalstrLength},
{{CDF_MaybeBuiltin, "strnlen", 2}, &CStringChecker::evalstrnLength},
{{CDF_MaybeBuiltin, "wcsnlen", 2}, &CStringChecker::evalstrnLength},
{{CDF_MaybeBuiltin, "strcmp", 2}, &CStringChecker::evalStrcmp},
{{CDF_MaybeBuiltin, "strncmp", 3}, &CStringChecker::evalStrncmp},
{{CDF_MaybeBuiltin, "strcasecmp", 2}, &CStringChecker::evalStrcasecmp},
{{CDF_MaybeBuiltin, "strncasecmp", 3}, &CStringChecker::evalStrncasecmp},
{{CDF_MaybeBuiltin, "strsep", 2}, &CStringChecker::evalStrsep},
{{CDF_MaybeBuiltin, "bcopy", 3}, &CStringChecker::evalBcopy},
{{CDF_MaybeBuiltin, "bcmp", 3}, &CStringChecker::evalMemcmp},
{{CDF_MaybeBuiltin, "bzero", 2}, &CStringChecker::evalBzero},
{{CDF_MaybeBuiltin, "explicit_bzero", 2}, &CStringChecker::evalBzero},
};
CallDescription StdCopy{{"std", "copy"}, 3},
StdCopyBackward{{"std", "copy_backward"}, 3};
FnCheck identifyCall(const CallEvent &Call, CheckerContext &C) const;
void evalMemcpy(CheckerContext &C, const CallExpr *CE, bool IsWide) const;
void evalMempcpy(CheckerContext &C, const CallExpr *CE) const;
void evalMemmove(CheckerContext &C, const CallExpr *CE) const;
void evalBcopy(CheckerContext &C, const CallExpr *CE) const;
void evalCopyCommon(CheckerContext &C, const CallExpr *CE,
ProgramStateRef state, SizeArgExpr Size,
DestinationArgExpr Dest, SourceArgExpr Source,
bool Restricted, bool IsMempcpy, bool IsWide) const;
void evalMemcmp(CheckerContext &C, const CallExpr *CE) const;
void evalstrLength(CheckerContext &C, const CallExpr *CE) const;
void evalstrnLength(CheckerContext &C, const CallExpr *CE) const;
void evalstrLengthCommon(CheckerContext &C,
const CallExpr *CE,
bool IsStrnlen = false) const;
void evalStrcpy(CheckerContext &C, const CallExpr *CE) const;
void evalStrncpy(CheckerContext &C, const CallExpr *CE) const;
void evalStpcpy(CheckerContext &C, const CallExpr *CE) const;
void evalStrlcpy(CheckerContext &C, const CallExpr *CE) const;
void evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, bool ReturnEnd,
bool IsBounded, ConcatFnKind appendK,
bool returnPtr = true) const;
void evalStrcat(CheckerContext &C, const CallExpr *CE) const;
void evalStrncat(CheckerContext &C, const CallExpr *CE) const;
void evalStrlcat(CheckerContext &C, const CallExpr *CE) const;
void evalStrcmp(CheckerContext &C, const CallExpr *CE) const;
void evalStrncmp(CheckerContext &C, const CallExpr *CE) const;
void evalStrcasecmp(CheckerContext &C, const CallExpr *CE) const;
void evalStrncasecmp(CheckerContext &C, const CallExpr *CE) const;
void evalStrcmpCommon(CheckerContext &C,
const CallExpr *CE,
bool IsBounded = false,
bool IgnoreCase = false) const;
void evalStrsep(CheckerContext &C, const CallExpr *CE) const;
void evalStdCopy(CheckerContext &C, const CallExpr *CE) const;
void evalStdCopyBackward(CheckerContext &C, const CallExpr *CE) const;
void evalStdCopyCommon(CheckerContext &C, const CallExpr *CE) const;
void evalMemset(CheckerContext &C, const CallExpr *CE) const;
void evalBzero(CheckerContext &C, const CallExpr *CE) const;
std::pair<ProgramStateRef , ProgramStateRef >
static assumeZero(CheckerContext &C,
ProgramStateRef state, SVal V, QualType Ty);
static ProgramStateRef setCStringLength(ProgramStateRef state,
const MemRegion *MR,
SVal strLength);
static SVal getCStringLengthForRegion(CheckerContext &C,
ProgramStateRef &state,
const Expr *Ex,
const MemRegion *MR,
bool hypothetical);
SVal getCStringLength(CheckerContext &C,
ProgramStateRef &state,
const Expr *Ex,
SVal Buf,
bool hypothetical = false) const;
const StringLiteral *getCStringLiteral(CheckerContext &C,
ProgramStateRef &state,
const Expr *expr,
SVal val) const;
static ProgramStateRef InvalidateBuffer(CheckerContext &C,
ProgramStateRef state,
const Expr *Ex, SVal V,
bool IsSourceBuffer,
const Expr *Size);
static bool SummarizeRegion(raw_ostream &os, ASTContext &Ctx,
const MemRegion *MR);
static bool memsetAux(const Expr *DstBuffer, SVal CharE,
const Expr *Size, CheckerContext &C,
ProgramStateRef &State);
ProgramStateRef checkNonNull(CheckerContext &C, ProgramStateRef State,
AnyArgExpr Arg, SVal l) const;
ProgramStateRef CheckLocation(CheckerContext &C, ProgramStateRef state,
AnyArgExpr Buffer, SVal Element,
AccessKind Access, bool IsWide = false) const;
ProgramStateRef CheckBufferAccess(CheckerContext &C, ProgramStateRef State,
AnyArgExpr Buffer, SizeArgExpr Size,
AccessKind Access,
bool IsWide = false) const;
ProgramStateRef CheckOverlap(CheckerContext &C, ProgramStateRef state,
SizeArgExpr Size, AnyArgExpr First,
AnyArgExpr Second, bool IsWide = false) const;
void emitOverlapBug(CheckerContext &C,
ProgramStateRef state,
const Stmt *First,
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;
void emitUninitializedReadBug(CheckerContext &C, ProgramStateRef State,
const Expr *E) const;
ProgramStateRef checkAdditionOverflow(CheckerContext &C,
ProgramStateRef state,
NonLoc left,
NonLoc right) const;
static bool IsFirstBufInBound(CheckerContext &C,
ProgramStateRef state,
const Expr *FirstBuf,
const Expr *Size);
};
}
REGISTER_MAP_WITH_PROGRAMSTATE(CStringLength, const MemRegion *, SVal)
std::pair<ProgramStateRef , ProgramStateRef >
CStringChecker::assumeZero(CheckerContext &C, ProgramStateRef state, SVal V,
QualType Ty) {
Optional<DefinedSVal> val = V.getAs<DefinedSVal>();
if (!val)
return std::pair<ProgramStateRef , ProgramStateRef >(state, state);
SValBuilder &svalBuilder = C.getSValBuilder();
DefinedOrUnknownSVal zero = svalBuilder.makeZeroVal(Ty);
return state->assume(svalBuilder.evalEQ(state, *val, zero));
}
ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C,
ProgramStateRef State,
AnyArgExpr Arg, SVal l) const {
if (!State)
return nullptr;
ProgramStateRef stateNull, stateNonNull;
std::tie(stateNull, stateNonNull) =
assumeZero(C, State, l, Arg.Expression->getType());
if (stateNull && !stateNonNull) {
if (Filter.CheckCStringNullArg) {
SmallString<80> buf;
llvm::raw_svector_ostream OS(buf);
assert(CurrentFunctionDescription);
OS << "Null pointer passed as " << (Arg.ArgumentIndex + 1)
<< llvm::getOrdinalSuffix(Arg.ArgumentIndex + 1) << " argument to "
<< CurrentFunctionDescription;
emitNullArgBug(C, stateNull, Arg.Expression, OS.str());
}
return nullptr;
}
assert(stateNonNull);
return stateNonNull;
}
ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
ProgramStateRef state,
AnyArgExpr Buffer, SVal Element,
AccessKind Access,
bool IsWide) const {
if (!state)
return nullptr;
const MemRegion *R = Element.getAsRegion();
if (!R)
return state;
const auto *ER = dyn_cast<ElementRegion>(R);
if (!ER)
return state;
SValBuilder &svalBuilder = C.getSValBuilder();
ASTContext &Ctx = svalBuilder.getContext();
NonLoc Idx = ER->getIndex();
if (!IsWide) {
if (ER->getValueType() != Ctx.CharTy)
return state;
} else {
if (ER->getValueType() != Ctx.WideCharTy)
return state;
QualType SizeTy = Ctx.getSizeType();
NonLoc WideSize =
svalBuilder
.makeIntVal(Ctx.getTypeSizeInChars(Ctx.WideCharTy).getQuantity(),
SizeTy)
.castAs<NonLoc>();
SVal Offset = svalBuilder.evalBinOpNN(state, BO_Mul, Idx, WideSize, SizeTy);
if (Offset.isUnknown())
return state;
Idx = Offset.castAs<NonLoc>();
}
const auto *superReg = cast<SubRegion>(ER->getSuperRegion());
DefinedOrUnknownSVal Size =
getDynamicExtent(state, superReg, C.getSValBuilder());
ProgramStateRef StInBound, StOutBound;
std::tie(StInBound, StOutBound) = state->assumeInBoundDual(Idx, Size);
if (StOutBound && !StInBound) {
if (!Filter.CheckCStringOutOfBounds)
return nullptr;
ErrorMessage Message =
createOutOfBoundErrorMsg(CurrentFunctionDescription, Access);
emitOutOfBoundsBug(C, StOutBound, Buffer.Expression, Message);
return nullptr;
}
if (Access == AccessKind::read) {
if (Filter.CheckCStringUninitializedRead &&
StInBound->getSVal(ER).isUndef()) {
emitUninitializedReadBug(C, StInBound, Buffer.Expression);
return nullptr;
}
}
return StInBound;
}
ProgramStateRef
CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State,
AnyArgExpr Buffer, SizeArgExpr Size,
AccessKind Access, bool IsWide) const {
if (!State)
return nullptr;
SValBuilder &svalBuilder = C.getSValBuilder();
ASTContext &Ctx = svalBuilder.getContext();
QualType SizeTy = Size.Expression->getType();
QualType PtrTy = Ctx.getPointerType(IsWide ? Ctx.WideCharTy : Ctx.CharTy);
SVal BufVal = C.getSVal(Buffer.Expression);
State = checkNonNull(C, State, Buffer, BufVal);
if (!State)
return nullptr;
if (!Filter.CheckCStringOutOfBounds)
return State;
SVal LengthVal = C.getSVal(Size.Expression);
Optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
if (!Length)
return State;
NonLoc One = svalBuilder.makeIntVal(1, SizeTy).castAs<NonLoc>();
SVal Offset = svalBuilder.evalBinOpNN(State, BO_Sub, *Length, One, SizeTy);
if (Offset.isUnknown())
return nullptr;
NonLoc LastOffset = Offset.castAs<NonLoc>();
SVal BufStart =
svalBuilder.evalCast(BufVal, PtrTy, Buffer.Expression->getType());
if (Optional<Loc> BufLoc = BufStart.getAs<Loc>()) {
SVal BufEnd =
svalBuilder.evalBinOpLN(State, BO_Add, *BufLoc, LastOffset, PtrTy);
State = CheckLocation(C, State, Buffer, BufEnd, Access, IsWide);
if (!State)
return nullptr;
}
return State;
}
ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
ProgramStateRef state,
SizeArgExpr Size, AnyArgExpr First,
AnyArgExpr Second,
bool IsWide) const {
if (!Filter.CheckCStringBufferOverlap)
return state;
if (!state)
return nullptr;
ProgramStateRef stateTrue, stateFalse;
if (First.Expression->getType()->getPointeeType().getAddressSpace() !=
Second.Expression->getType()->getPointeeType().getAddressSpace())
return state;
const LocationContext *LCtx = C.getLocationContext();
SVal firstVal = state->getSVal(First.Expression, LCtx);
SVal secondVal = state->getSVal(Second.Expression, LCtx);
Optional<Loc> firstLoc = firstVal.getAs<Loc>();
if (!firstLoc)
return state;
Optional<Loc> secondLoc = secondVal.getAs<Loc>();
if (!secondLoc)
return state;
SValBuilder &svalBuilder = C.getSValBuilder();
std::tie(stateTrue, stateFalse) =
state->assume(svalBuilder.evalEQ(state, *firstLoc, *secondLoc));
if (stateTrue && !stateFalse) {
emitOverlapBug(C, stateTrue, First.Expression, Second.Expression);
return nullptr;
}
assert(stateFalse);
state = stateFalse;
QualType cmpTy = svalBuilder.getConditionType();
SVal reverse =
svalBuilder.evalBinOpLL(state, BO_GT, *firstLoc, *secondLoc, cmpTy);
Optional<DefinedOrUnknownSVal> reverseTest =
reverse.getAs<DefinedOrUnknownSVal>();
if (!reverseTest)
return state;
std::tie(stateTrue, stateFalse) = state->assume(*reverseTest);
if (stateTrue) {
if (stateFalse) {
return state;
} else {
std::swap(firstLoc, secondLoc);
std::swap(First, Second);
}
}
SVal LengthVal = state->getSVal(Size.Expression, LCtx);
Optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
if (!Length)
return state;
ASTContext &Ctx = svalBuilder.getContext();
QualType CharPtrTy = Ctx.getPointerType(IsWide ? Ctx.WideCharTy : Ctx.CharTy);
SVal FirstStart =
svalBuilder.evalCast(*firstLoc, CharPtrTy, First.Expression->getType());
Optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>();
if (!FirstStartLoc)
return state;
SVal FirstEnd = svalBuilder.evalBinOpLN(state, BO_Add, *FirstStartLoc,
*Length, CharPtrTy);
Optional<Loc> FirstEndLoc = FirstEnd.getAs<Loc>();
if (!FirstEndLoc)
return state;
SVal Overlap =
svalBuilder.evalBinOpLL(state, BO_GT, *FirstEndLoc, *secondLoc, cmpTy);
Optional<DefinedOrUnknownSVal> OverlapTest =
Overlap.getAs<DefinedOrUnknownSVal>();
if (!OverlapTest)
return state;
std::tie(stateTrue, stateFalse) = state->assume(*OverlapTest);
if (stateTrue && !stateFalse) {
emitOverlapBug(C, stateTrue, First.Expression, Second.Expression);
return nullptr;
}
assert(stateFalse);
return stateFalse;
}
void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state,
const Stmt *First, const Stmt *Second) const {
ExplodedNode *N = C.generateErrorNode(state);
if (!N)
return;
if (!BT_Overlap)
BT_Overlap.reset(new BugType(Filter.CheckNameCStringBufferOverlap,
categories::UnixAPI, "Improper arguments"));
auto report = std::make_unique<PathSensitiveBugReport>(
*BT_Overlap, "Arguments must not be overlapping buffers", N);
report->addRange(First->getSourceRange());
report->addRange(Second->getSourceRange());
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 = std::make_unique<PathSensitiveBugReport>(*BT, WarningMsg, N);
Report->addRange(S->getSourceRange());
if (const auto *Ex = dyn_cast<Expr>(S))
bugreporter::trackExpressionValue(N, Ex, *Report);
C.emitReport(std::move(Report));
}
}
void CStringChecker::emitUninitializedReadBug(CheckerContext &C,
ProgramStateRef State,
const Expr *E) const {
if (ExplodedNode *N = C.generateErrorNode(State)) {
const char *Msg =
"Bytes string function accesses uninitialized/garbage values";
if (!BT_UninitRead)
BT_UninitRead.reset(
new BuiltinBug(Filter.CheckNameCStringUninitializedRead,
"Accessing unitialized/garbage values", Msg));
BuiltinBug *BT = static_cast<BuiltinBug *>(BT_UninitRead.get());
auto Report = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
Report->addRange(E->getSourceRange());
bugreporter::trackExpressionValue(N, E, *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());
auto Report = std::make_unique<PathSensitiveBugReport>(*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 =
std::make_unique<PathSensitiveBugReport>(*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_AdditionOverflow)
BT_AdditionOverflow.reset(
new BuiltinBug(Filter.CheckNameCStringOutOfBounds, "API",
"Sum of expressions causes overflow."));
const char *WarningMsg =
"This expression will create a string whose length is too big to "
"be represented as a size_t";
auto Report = std::make_unique<PathSensitiveBugReport>(*BT_AdditionOverflow,
WarningMsg, N);
C.emitReport(std::move(Report));
}
}
ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C,
ProgramStateRef state,
NonLoc left,
NonLoc right) const {
if (!Filter.CheckCStringOutOfBounds)
return state;
if (!state)
return nullptr;
SValBuilder &svalBuilder = C.getSValBuilder();
BasicValueFactory &BVF = svalBuilder.getBasicValueFactory();
QualType sizeTy = svalBuilder.getContext().getSizeType();
const llvm::APSInt &maxValInt = BVF.getMaxValue(sizeTy);
NonLoc maxVal = svalBuilder.makeIntVal(maxValInt);
SVal maxMinusRight;
if (isa<nonloc::ConcreteInt>(right)) {
maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, right,
sizeTy);
} else {
maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, left,
sizeTy);
left = right;
}
if (Optional<NonLoc> maxMinusRightNL = maxMinusRight.getAs<NonLoc>()) {
QualType cmpTy = svalBuilder.getConditionType();
SVal willOverflow = svalBuilder.evalBinOpNN(state, BO_GT, left,
*maxMinusRightNL, cmpTy);
ProgramStateRef stateOverflow, stateOkay;
std::tie(stateOverflow, stateOkay) =
state->assume(willOverflow.castAs<DefinedOrUnknownSVal>());
if (stateOverflow && !stateOkay) {
emitAdditionOverflowBug(C, stateOverflow);
return nullptr;
}
assert(stateOkay);
state = stateOkay;
}
return state;
}
ProgramStateRef CStringChecker::setCStringLength(ProgramStateRef state,
const MemRegion *MR,
SVal strLength) {
assert(!strLength.isUndef() && "Attempt to set an undefined string length");
MR = MR->StripCasts();
switch (MR->getKind()) {
case MemRegion::StringRegionKind:
return state;
case MemRegion::SymbolicRegionKind:
case MemRegion::AllocaRegionKind:
case MemRegion::NonParamVarRegionKind:
case MemRegion::ParamVarRegionKind:
case MemRegion::FieldRegionKind:
case MemRegion::ObjCIvarRegionKind:
break;
case MemRegion::ElementRegionKind:
return state;
default:
return state;
}
if (strLength.isUnknown())
return state->remove<CStringLength>(MR);
return state->set<CStringLength>(MR, strLength);
}
SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C,
ProgramStateRef &state,
const Expr *Ex,
const MemRegion *MR,
bool hypothetical) {
if (!hypothetical) {
const SVal *Recorded = state->get<CStringLength>(MR);
if (Recorded)
return *Recorded;
}
SValBuilder &svalBuilder = C.getSValBuilder();
QualType sizeTy = svalBuilder.getContext().getSizeType();
SVal strLength = svalBuilder.getMetadataSymbolVal(CStringChecker::getTag(),
MR, Ex, sizeTy,
C.getLocationContext(),
C.blockCount());
if (!hypothetical) {
if (Optional<NonLoc> strLn = strLength.getAs<NonLoc>()) {
BasicValueFactory &BVF = svalBuilder.getBasicValueFactory();
const llvm::APSInt &maxValInt = BVF.getMaxValue(sizeTy);
llvm::APSInt fourInt = APSIntType(maxValInt).getValue(4);
const llvm::APSInt *maxLengthInt = BVF.evalAPSInt(BO_Div, maxValInt,
fourInt);
NonLoc maxLength = svalBuilder.makeIntVal(*maxLengthInt);
SVal evalLength = svalBuilder.evalBinOpNN(state, BO_LE, *strLn,
maxLength, sizeTy);
state = state->assume(evalLength.castAs<DefinedOrUnknownSVal>(), true);
}
state = state->set<CStringLength>(MR, strLength);
}
return strLength;
}
SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
const Expr *Ex, SVal Buf,
bool hypothetical) const {
const MemRegion *MR = Buf.getAsRegion();
if (!MR) {
if (Optional<loc::GotoLabel> Label = Buf.getAs<loc::GotoLabel>()) {
if (Filter.CheckCStringNotNullTerm) {
SmallString<120> buf;
llvm::raw_svector_ostream os(buf);
assert(CurrentFunctionDescription);
os << "Argument to " << CurrentFunctionDescription
<< " is the address of the label '" << Label->getLabel()->getName()
<< "', which is not a null-terminated string";
emitNotCStringBug(C, state, Ex, os.str());
}
return UndefinedVal();
}
return UnknownVal();
}
MR = MR->StripCasts();
switch (MR->getKind()) {
case MemRegion::StringRegionKind: {
SValBuilder &svalBuilder = C.getSValBuilder();
QualType sizeTy = svalBuilder.getContext().getSizeType();
const StringLiteral *strLit = cast<StringRegion>(MR)->getStringLiteral();
return svalBuilder.makeIntVal(strLit->getLength(), sizeTy);
}
case MemRegion::SymbolicRegionKind:
case MemRegion::AllocaRegionKind:
case MemRegion::NonParamVarRegionKind:
case MemRegion::ParamVarRegionKind:
case MemRegion::FieldRegionKind:
case MemRegion::ObjCIvarRegionKind:
return getCStringLengthForRegion(C, state, Ex, MR, hypothetical);
case MemRegion::CompoundLiteralRegionKind:
return UnknownVal();
case MemRegion::ElementRegionKind:
return UnknownVal();
default:
if (Filter.CheckCStringNotNullTerm) {
SmallString<120> buf;
llvm::raw_svector_ostream os(buf);
assert(CurrentFunctionDescription);
os << "Argument to " << CurrentFunctionDescription << " is ";
if (SummarizeRegion(os, C.getASTContext(), MR))
os << ", which is not a null-terminated string";
else
os << "not a null-terminated string";
emitNotCStringBug(C, state, Ex, os.str());
}
return UndefinedVal();
}
}
const StringLiteral *CStringChecker::getCStringLiteral(CheckerContext &C,
ProgramStateRef &state, const Expr *expr, SVal val) const {
const MemRegion *bufRegion = val.getAsRegion();
if (!bufRegion)
return nullptr;
bufRegion = bufRegion->StripCasts();
const StringRegion *strRegion= dyn_cast<StringRegion>(bufRegion);
if (!strRegion)
return nullptr;
return strRegion->getStringLiteral();
}
bool CStringChecker::IsFirstBufInBound(CheckerContext &C,
ProgramStateRef state,
const Expr *FirstBuf,
const Expr *Size) {
SValBuilder &svalBuilder = C.getSValBuilder();
ASTContext &Ctx = svalBuilder.getContext();
const LocationContext *LCtx = C.getLocationContext();
QualType sizeTy = Size->getType();
QualType PtrTy = Ctx.getPointerType(Ctx.CharTy);
SVal BufVal = state->getSVal(FirstBuf, LCtx);
SVal LengthVal = state->getSVal(Size, LCtx);
Optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
if (!Length)
return true;
NonLoc One = svalBuilder.makeIntVal(1, sizeTy).castAs<NonLoc>();
SVal Offset = svalBuilder.evalBinOpNN(state, BO_Sub, *Length, One, sizeTy);
if (Offset.isUnknown())
return true; NonLoc LastOffset = Offset.castAs<NonLoc>();
SVal BufStart = svalBuilder.evalCast(BufVal, PtrTy, FirstBuf->getType());
Optional<Loc> BufLoc = BufStart.getAs<Loc>();
if (!BufLoc)
return true;
SVal BufEnd =
svalBuilder.evalBinOpLN(state, BO_Add, *BufLoc, LastOffset, PtrTy);
const MemRegion *R = BufEnd.getAsRegion();
if (!R)
return true;
const ElementRegion *ER = dyn_cast<ElementRegion>(R);
if (!ER)
return true;
assert(ER->getValueType() == C.getASTContext().CharTy &&
"IsFirstBufInBound should only be called with char* ElementRegions");
const SubRegion *superReg = cast<SubRegion>(ER->getSuperRegion());
DefinedOrUnknownSVal SizeDV = getDynamicExtent(state, superReg, svalBuilder);
DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
ProgramStateRef StInBound = state->assumeInBound(Idx, SizeDV, true);
return static_cast<bool>(StInBound);
}
ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C,
ProgramStateRef state,
const Expr *E, SVal V,
bool IsSourceBuffer,
const Expr *Size) {
Optional<Loc> L = V.getAs<Loc>();
if (!L)
return state;
if (Optional<loc::MemRegionVal> MR = L->getAs<loc::MemRegionVal>()) {
const MemRegion *R = MR->getRegion()->StripCasts();
if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
R = ER->getSuperRegion();
}
const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
bool CausesPointerEscape = false;
RegionAndSymbolInvalidationTraits ITraits;
if (IsSourceBuffer) {
ITraits.setTrait(R->getBaseRegion(),
RegionAndSymbolInvalidationTraits::TK_PreserveContents);
ITraits.setTrait(R, RegionAndSymbolInvalidationTraits::TK_SuppressEscape);
CausesPointerEscape = true;
} else {
const MemRegion::Kind& K = R->getKind();
if (K == MemRegion::FieldRegionKind)
if (Size && IsFirstBufInBound(C, state, E, Size)) {
ITraits.setTrait(
R,
RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion);
}
}
return state->invalidateRegions(R, E, C.blockCount(), LCtx,
CausesPointerEscape, nullptr, nullptr,
&ITraits);
}
return state->killBinding(*L);
}
bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx,
const MemRegion *MR) {
switch (MR->getKind()) {
case MemRegion::FunctionCodeRegionKind: {
if (const auto *FD = cast<FunctionCodeRegion>(MR)->getDecl())
os << "the address of the function '" << *FD << '\'';
else
os << "the address of a function";
return true;
}
case MemRegion::BlockCodeRegionKind:
os << "block text";
return true;
case MemRegion::BlockDataRegionKind:
os << "a block";
return true;
case MemRegion::CXXThisRegionKind:
case MemRegion::CXXTempObjectRegionKind:
os << "a C++ temp object of type "
<< cast<TypedValueRegion>(MR)->getValueType();
return true;
case MemRegion::NonParamVarRegionKind:
os << "a variable of type" << cast<TypedValueRegion>(MR)->getValueType();
return true;
case MemRegion::ParamVarRegionKind:
os << "a parameter of type" << cast<TypedValueRegion>(MR)->getValueType();
return true;
case MemRegion::FieldRegionKind:
os << "a field of type " << cast<TypedValueRegion>(MR)->getValueType();
return true;
case MemRegion::ObjCIvarRegionKind:
os << "an instance variable of type "
<< cast<TypedValueRegion>(MR)->getValueType();
return true;
default:
return false;
}
}
bool CStringChecker::memsetAux(const Expr *DstBuffer, SVal CharVal,
const Expr *Size, CheckerContext &C,
ProgramStateRef &State) {
SVal MemVal = C.getSVal(DstBuffer);
SVal SizeVal = C.getSVal(Size);
const MemRegion *MR = MemVal.getAsRegion();
if (!MR)
return false;
RegionOffset Offset = MR->getAsOffset();
const MemRegion *BR = Offset.getRegion();
Optional<NonLoc> SizeNL = SizeVal.getAs<NonLoc>();
if (!SizeNL)
return false;
SValBuilder &svalBuilder = C.getSValBuilder();
ASTContext &Ctx = C.getASTContext();
if (Offset.isValid() && !Offset.hasSymbolicOffset() &&
Offset.getOffset() == 0) {
DefinedOrUnknownSVal SizeDV = getDynamicExtent(State, BR, svalBuilder);
ProgramStateRef StateWholeReg, StateNotWholeReg;
std::tie(StateWholeReg, StateNotWholeReg) =
State->assume(svalBuilder.evalEQ(State, SizeDV, *SizeNL));
CharVal = svalBuilder.evalCast(CharVal, Ctx.UnsignedCharTy, Ctx.IntTy);
ProgramStateRef StateNullChar, StateNonNullChar;
std::tie(StateNullChar, StateNonNullChar) =
assumeZero(C, State, CharVal, Ctx.UnsignedCharTy);
if (StateWholeReg && !StateNotWholeReg && StateNullChar &&
!StateNonNullChar) {
State = State->bindDefaultZero(svalBuilder.makeLoc(BR),
C.getLocationContext());
} else {
State = InvalidateBuffer(C, State, DstBuffer, MemVal,
false, Size);
}
if (StateNullChar && !StateNonNullChar) {
State = setCStringLength(State, MR,
svalBuilder.makeZeroVal(Ctx.getSizeType()));
} else if (!StateNullChar && StateNonNullChar) {
SVal NewStrLen = svalBuilder.getMetadataSymbolVal(
CStringChecker::getTag(), MR, DstBuffer, Ctx.getSizeType(),
C.getLocationContext(), C.blockCount());
SVal NewStrLenGESize = svalBuilder.evalBinOp(
State, BO_GE, NewStrLen, SizeVal, svalBuilder.getConditionType());
State = setCStringLength(
State->assume(NewStrLenGESize.castAs<DefinedOrUnknownSVal>(), true),
MR, NewStrLen);
}
} else {
State = InvalidateBuffer(C, State, DstBuffer, MemVal,
false, Size);
}
return true;
}
void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE,
ProgramStateRef state, SizeArgExpr Size,
DestinationArgExpr Dest,
SourceArgExpr Source, bool Restricted,
bool IsMempcpy, bool IsWide) const {
CurrentFunctionDescription = "memory copy function";
const LocationContext *LCtx = C.getLocationContext();
SVal sizeVal = state->getSVal(Size.Expression, LCtx);
QualType sizeTy = Size.Expression->getType();
ProgramStateRef stateZeroSize, stateNonZeroSize;
std::tie(stateZeroSize, stateNonZeroSize) =
assumeZero(C, state, sizeVal, sizeTy);
SVal destVal = state->getSVal(Dest.Expression, LCtx);
if (stateZeroSize && !stateNonZeroSize) {
stateZeroSize = stateZeroSize->BindExpr(CE, LCtx, destVal);
C.addTransition(stateZeroSize);
return;
}
if (stateNonZeroSize) {
state = stateNonZeroSize;
state = checkNonNull(C, state, Dest, destVal);
if (!state)
return;
SVal srcVal = state->getSVal(Source.Expression, LCtx);
state = checkNonNull(C, state, Source, srcVal);
if (!state)
return;
state = CheckBufferAccess(C, state, Dest, Size, AccessKind::write, IsWide);
state = CheckBufferAccess(C, state, Source, Size, AccessKind::read, IsWide);
if (Restricted)
state = CheckOverlap(C, state, Size, Dest, Source, IsWide);
if (!state)
return;
if (IsMempcpy) {
SValBuilder &SvalBuilder = C.getSValBuilder();
ASTContext &Ctx = SvalBuilder.getContext();
QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy);
SVal DestRegCharVal =
SvalBuilder.evalCast(destVal, CharPtrTy, Dest.Expression->getType());
SVal lastElement = C.getSValBuilder().evalBinOp(
state, BO_Add, DestRegCharVal, sizeVal, Dest.Expression->getType());
if (lastElement.isUnknown())
lastElement = C.getSValBuilder().conjureSymbolVal(nullptr, CE, LCtx,
C.blockCount());
state = state->BindExpr(CE, LCtx, lastElement);
} else {
state = state->BindExpr(CE, LCtx, destVal);
}
state =
InvalidateBuffer(C, state, Dest.Expression, C.getSVal(Dest.Expression),
false, Size.Expression);
state = InvalidateBuffer(C, state, Source.Expression,
C.getSVal(Source.Expression),
true, nullptr);
C.addTransition(state);
}
}
void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE,
bool IsWide) const {
DestinationArgExpr Dest = {CE->getArg(0), 0};
SourceArgExpr Src = {CE->getArg(1), 1};
SizeArgExpr Size = {CE->getArg(2), 2};
ProgramStateRef State = C.getState();
constexpr bool IsRestricted = true;
constexpr bool IsMempcpy = false;
evalCopyCommon(C, CE, State, Size, Dest, Src, IsRestricted, IsMempcpy,
IsWide);
}
void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const {
DestinationArgExpr Dest = {CE->getArg(0), 0};
SourceArgExpr Src = {CE->getArg(1), 1};
SizeArgExpr Size = {CE->getArg(2), 2};
constexpr bool IsRestricted = true;
constexpr bool IsMempcpy = true;
evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy,
false);
}
void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const {
DestinationArgExpr Dest = {CE->getArg(0), 0};
SourceArgExpr Src = {CE->getArg(1), 1};
SizeArgExpr Size = {CE->getArg(2), 2};
constexpr bool IsRestricted = false;
constexpr bool IsMempcpy = false;
evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy,
false);
}
void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const {
SourceArgExpr Src(CE->getArg(0), 0);
DestinationArgExpr Dest = {CE->getArg(1), 1};
SizeArgExpr Size = {CE->getArg(2), 2};
constexpr bool IsRestricted = false;
constexpr bool IsMempcpy = false;
evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy,
false);
}
void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const {
CurrentFunctionDescription = "memory comparison function";
AnyArgExpr Left = {CE->getArg(0), 0};
AnyArgExpr Right = {CE->getArg(1), 1};
SizeArgExpr Size = {CE->getArg(2), 2};
ProgramStateRef State = C.getState();
SValBuilder &Builder = C.getSValBuilder();
const LocationContext *LCtx = C.getLocationContext();
SVal sizeVal = State->getSVal(Size.Expression, LCtx);
QualType sizeTy = Size.Expression->getType();
ProgramStateRef stateZeroSize, stateNonZeroSize;
std::tie(stateZeroSize, stateNonZeroSize) =
assumeZero(C, State, sizeVal, sizeTy);
if (stateZeroSize) {
State = stateZeroSize;
State = State->BindExpr(CE, LCtx, Builder.makeZeroVal(CE->getType()));
C.addTransition(State);
}
if (stateNonZeroSize) {
State = stateNonZeroSize;
DefinedOrUnknownSVal LV =
State->getSVal(Left.Expression, LCtx).castAs<DefinedOrUnknownSVal>();
DefinedOrUnknownSVal RV =
State->getSVal(Right.Expression, LCtx).castAs<DefinedOrUnknownSVal>();
ProgramStateRef SameBuffer, NotSameBuffer;
std::tie(SameBuffer, NotSameBuffer) =
State->assume(Builder.evalEQ(State, LV, RV));
if (SameBuffer && !NotSameBuffer) {
State = SameBuffer;
State = CheckBufferAccess(C, State, Left, Size, AccessKind::read);
if (State) {
State =
SameBuffer->BindExpr(CE, LCtx, Builder.makeZeroVal(CE->getType()));
C.addTransition(State);
}
return;
}
assert(NotSameBuffer);
State = CheckBufferAccess(C, State, Right, Size, AccessKind::read);
State = CheckBufferAccess(C, State, Left, Size, AccessKind::read);
if (State) {
SVal CmpV = Builder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
State = State->BindExpr(CE, LCtx, CmpV);
C.addTransition(State);
}
}
}
void CStringChecker::evalstrLength(CheckerContext &C,
const CallExpr *CE) const {
evalstrLengthCommon(C, CE, false);
}
void CStringChecker::evalstrnLength(CheckerContext &C,
const CallExpr *CE) const {
evalstrLengthCommon(C, CE, true);
}
void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
bool IsStrnlen) const {
CurrentFunctionDescription = "string length function";
ProgramStateRef state = C.getState();
const LocationContext *LCtx = C.getLocationContext();
if (IsStrnlen) {
const Expr *maxlenExpr = CE->getArg(1);
SVal maxlenVal = state->getSVal(maxlenExpr, LCtx);
ProgramStateRef stateZeroSize, stateNonZeroSize;
std::tie(stateZeroSize, stateNonZeroSize) =
assumeZero(C, state, maxlenVal, maxlenExpr->getType());
if (stateZeroSize) {
SVal zero = C.getSValBuilder().makeZeroVal(CE->getType());
stateZeroSize = stateZeroSize->BindExpr(CE, LCtx, zero);
C.addTransition(stateZeroSize);
}
if (!stateNonZeroSize)
return;
state = stateNonZeroSize;
}
AnyArgExpr Arg = {CE->getArg(0), 0};
SVal ArgVal = state->getSVal(Arg.Expression, LCtx);
state = checkNonNull(C, state, Arg, ArgVal);
if (!state)
return;
SVal strLength = getCStringLength(C, state, Arg.Expression, ArgVal);
if (strLength.isUndef())
return;
DefinedOrUnknownSVal result = UnknownVal();
if (IsStrnlen) {
QualType cmpTy = C.getSValBuilder().getConditionType();
const Expr *maxlenExpr = CE->getArg(1);
SVal maxlenVal = state->getSVal(maxlenExpr, LCtx);
Optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>();
Optional<NonLoc> maxlenValNL = maxlenVal.getAs<NonLoc>();
if (strLengthNL && maxlenValNL) {
ProgramStateRef stateStringTooLong, stateStringNotTooLong;
std::tie(stateStringTooLong, stateStringNotTooLong) = state->assume(
C.getSValBuilder()
.evalBinOpNN(state, BO_GT, *strLengthNL, *maxlenValNL, cmpTy)
.castAs<DefinedOrUnknownSVal>());
if (stateStringTooLong && !stateStringNotTooLong) {
result = *maxlenValNL;
} else if (stateStringNotTooLong && !stateStringTooLong) {
result = *strLengthNL;
}
}
if (result.isUnknown()) {
result = C.getSValBuilder().conjureSymbolVal(nullptr, CE, LCtx,
C.blockCount());
NonLoc resultNL = result.castAs<NonLoc>();
if (strLengthNL) {
state = state->assume(C.getSValBuilder().evalBinOpNN(
state, BO_LE, resultNL, *strLengthNL, cmpTy)
.castAs<DefinedOrUnknownSVal>(), true);
}
if (maxlenValNL) {
state = state->assume(C.getSValBuilder().evalBinOpNN(
state, BO_LE, resultNL, *maxlenValNL, cmpTy)
.castAs<DefinedOrUnknownSVal>(), true);
}
}
} else {
result = strLength.castAs<DefinedOrUnknownSVal>();
if (result.isUnknown()) {
result = C.getSValBuilder().conjureSymbolVal(nullptr, CE, LCtx,
C.blockCount());
}
}
assert(!result.isUnknown() && "Should have conjured a value by now");
state = state->BindExpr(CE, LCtx, result);
C.addTransition(state);
}
void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) const {
evalStrcpyCommon(C, CE,
false,
false,
ConcatFnKind::none);
}
void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) const {
evalStrcpyCommon(C, CE,
false,
true,
ConcatFnKind::none);
}
void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) const {
evalStrcpyCommon(C, CE,
true,
false,
ConcatFnKind::none);
}
void CStringChecker::evalStrlcpy(CheckerContext &C, const CallExpr *CE) const {
evalStrcpyCommon(C, CE,
true,
true,
ConcatFnKind::none,
false);
}
void CStringChecker::evalStrcat(CheckerContext &C, const CallExpr *CE) const {
evalStrcpyCommon(C, CE,
false,
false,
ConcatFnKind::strcat);
}
void CStringChecker::evalStrncat(CheckerContext &C, const CallExpr *CE) const {
evalStrcpyCommon(C, CE,
false,
true,
ConcatFnKind::strcat);
}
void CStringChecker::evalStrlcat(CheckerContext &C, const CallExpr *CE) const {
evalStrcpyCommon(C, CE,
false,
true,
ConcatFnKind::strlcat,
false);
}
void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
bool ReturnEnd, bool IsBounded,
ConcatFnKind appendK,
bool returnPtr) const {
if (appendK == ConcatFnKind::none)
CurrentFunctionDescription = "string copy function";
else
CurrentFunctionDescription = "string concatenation function";
ProgramStateRef state = C.getState();
const LocationContext *LCtx = C.getLocationContext();
DestinationArgExpr Dst = {CE->getArg(0), 0};
SVal DstVal = state->getSVal(Dst.Expression, LCtx);
state = checkNonNull(C, state, Dst, DstVal);
if (!state)
return;
SourceArgExpr srcExpr = {CE->getArg(1), 1};
SVal srcVal = state->getSVal(srcExpr.Expression, LCtx);
state = checkNonNull(C, state, srcExpr, srcVal);
if (!state)
return;
SVal strLength = getCStringLength(C, state, srcExpr.Expression, srcVal);
Optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>();
SVal dstStrLength = getCStringLength(C, state, Dst.Expression, DstVal);
Optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>();
if (strLength.isUndef())
return;
SValBuilder &svalBuilder = C.getSValBuilder();
QualType cmpTy = svalBuilder.getConditionType();
QualType sizeTy = svalBuilder.getContext().getSizeType();
SVal amountCopied = UnknownVal();
SVal maxLastElementIndex = UnknownVal();
const char *boundWarning = nullptr;
SizeArgExpr SrcExprAsSizeDummy = {srcExpr.Expression, srcExpr.ArgumentIndex};
state = CheckOverlap(
C, state,
(IsBounded ? SizeArgExpr{CE->getArg(2), 2} : SrcExprAsSizeDummy), Dst,
srcExpr);
if (!state)
return;
if (IsBounded) {
SizeArgExpr lenExpr = {CE->getArg(2), 2};
SVal lenVal = state->getSVal(lenExpr.Expression, LCtx);
lenVal =
svalBuilder.evalCast(lenVal, sizeTy, lenExpr.Expression->getType());
Optional<NonLoc> lenValNL = lenVal.getAs<NonLoc>();
if (strLengthNL && lenValNL) {
switch (appendK) {
case ConcatFnKind::none:
case ConcatFnKind::strcat: {
ProgramStateRef stateSourceTooLong, stateSourceNotTooLong;
std::tie(stateSourceTooLong, stateSourceNotTooLong) = state->assume(
svalBuilder
.evalBinOpNN(state, BO_GE, *strLengthNL, *lenValNL, cmpTy)
.castAs<DefinedOrUnknownSVal>());
if (stateSourceTooLong && !stateSourceNotTooLong) {
state = stateSourceTooLong;
amountCopied = lenVal;
} else if (!stateSourceTooLong && stateSourceNotTooLong) {
state = stateSourceNotTooLong;
amountCopied = strLength;
}
break;
}
case ConcatFnKind::strlcat:
if (!dstStrLengthNL)
return;
SVal freeSpace = svalBuilder.evalBinOpNN(state, BO_Sub, *lenValNL,
*dstStrLengthNL, sizeTy);
if (!isa<NonLoc>(freeSpace))
return;
freeSpace =
svalBuilder.evalBinOp(state, BO_Sub, freeSpace,
svalBuilder.makeIntVal(1, sizeTy), sizeTy);
Optional<NonLoc> freeSpaceNL = freeSpace.getAs<NonLoc>();
if (!freeSpaceNL)
return;
SVal hasEnoughSpace = svalBuilder.evalBinOpNN(
state, BO_LE, *strLengthNL, *freeSpaceNL, cmpTy);
ProgramStateRef TrueState, FalseState;
std::tie(TrueState, FalseState) =
state->assume(hasEnoughSpace.castAs<DefinedOrUnknownSVal>());
if (TrueState && !FalseState) {
amountCopied = strLength;
}
if (!TrueState && FalseState) {
amountCopied = freeSpace;
}
if (TrueState && FalseState)
amountCopied = UnknownVal();
break;
}
}
if (lenValNL) {
switch (appendK) {
case ConcatFnKind::strcat:
if (dstStrLength.isUndef())
return;
if (dstStrLengthNL) {
maxLastElementIndex = svalBuilder.evalBinOpNN(
state, BO_Add, *lenValNL, *dstStrLengthNL, sizeTy);
boundWarning = "Size argument is greater than the free space in the "
"destination buffer";
}
break;
case ConcatFnKind::none:
case ConcatFnKind::strlcat:
ProgramStateRef StateZeroSize, StateNonZeroSize;
std::tie(StateZeroSize, StateNonZeroSize) =
assumeZero(C, state, *lenValNL, sizeTy);
if (StateZeroSize && !StateNonZeroSize) {
if (returnPtr) {
StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, DstVal);
} else {
if (appendK == ConcatFnKind::none) {
StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, strLength);
} else {
SVal retSize = svalBuilder.evalBinOp(
state, BO_Add, strLength, dstStrLength, sizeTy);
StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, retSize);
}
}
C.addTransition(StateZeroSize);
return;
}
NonLoc one = svalBuilder.makeIntVal(1, sizeTy).castAs<NonLoc>();
maxLastElementIndex =
svalBuilder.evalBinOpNN(state, BO_Sub, *lenValNL, one, sizeTy);
boundWarning = "Size argument is greater than the length of the "
"destination buffer";
break;
}
}
} else {
amountCopied = strLength;
}
assert(state);
SVal finalStrLength = UnknownVal();
SVal strlRetVal = UnknownVal();
if (appendK == ConcatFnKind::none && !returnPtr) {
strlRetVal = strLength;
}
if (appendK != ConcatFnKind::none) {
if (dstStrLength.isUndef())
return;
if (appendK == ConcatFnKind::strlcat && dstStrLengthNL && strLengthNL) {
strlRetVal = svalBuilder.evalBinOpNN(state, BO_Add, *strLengthNL,
*dstStrLengthNL, sizeTy);
}
Optional<NonLoc> amountCopiedNL = amountCopied.getAs<NonLoc>();
if (amountCopiedNL && dstStrLengthNL) {
state = checkAdditionOverflow(C, state, *amountCopiedNL, *dstStrLengthNL);
if (!state)
return;
finalStrLength = svalBuilder.evalBinOpNN(state, BO_Add, *amountCopiedNL,
*dstStrLengthNL, sizeTy);
}
if (finalStrLength.isUnknown()) {
finalStrLength = getCStringLength(C, state, CE, DstVal, true);
assert(!finalStrLength.isUndef());
if (Optional<NonLoc> finalStrLengthNL = finalStrLength.getAs<NonLoc>()) {
if (amountCopiedNL && appendK == ConcatFnKind::none) {
SVal sourceInResult = svalBuilder.evalBinOpNN(
state, BO_GE, *finalStrLengthNL, *amountCopiedNL, cmpTy);
state = state->assume(sourceInResult.castAs<DefinedOrUnknownSVal>(),
true);
if (!state)
return;
}
if (dstStrLengthNL && appendK != ConcatFnKind::none) {
SVal destInResult = svalBuilder.evalBinOpNN(state, BO_GE,
*finalStrLengthNL,
*dstStrLengthNL,
cmpTy);
state =
state->assume(destInResult.castAs<DefinedOrUnknownSVal>(), true);
if (!state)
return;
}
}
}
} else {
finalStrLength = amountCopied;
}
SVal Result;
if (returnPtr) {
Result = (ReturnEnd ? UnknownVal() : DstVal);
} else {
if (appendK == ConcatFnKind::strlcat || appendK == ConcatFnKind::none)
Result = strlRetVal;
else
Result = finalStrLength;
}
assert(state);
if (Optional<loc::MemRegionVal> dstRegVal =
DstVal.getAs<loc::MemRegionVal>()) {
QualType ptrTy = Dst.Expression->getType();
if (Optional<NonLoc> maxLastNL = maxLastElementIndex.getAs<NonLoc>()) {
SVal maxLastElement =
svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal, *maxLastNL, ptrTy);
state = CheckLocation(C, state, Dst, maxLastElement, AccessKind::write);
if (!state)
return;
}
if (Optional<NonLoc> knownStrLength = finalStrLength.getAs<NonLoc>()) {
SVal lastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal,
*knownStrLength, ptrTy);
if (!boundWarning) {
state = CheckLocation(C, state, Dst, lastElement, AccessKind::write);
if (!state)
return;
}
if (returnPtr && ReturnEnd)
Result = lastElement;
}
state = InvalidateBuffer(C, state, Dst.Expression, *dstRegVal,
false, nullptr);
state = InvalidateBuffer(C, state, srcExpr.Expression, srcVal,
true, nullptr);
if (IsBounded && (appendK == ConcatFnKind::none)) {
if (amountCopied != strLength)
finalStrLength = UnknownVal();
}
state = setCStringLength(state, dstRegVal->getRegion(), finalStrLength);
}
assert(state);
if (returnPtr) {
if (ReturnEnd && Result.isUnknown()) {
Result = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
}
}
state = state->BindExpr(CE, LCtx, Result);
C.addTransition(state);
}
void CStringChecker::evalStrcmp(CheckerContext &C, const CallExpr *CE) const {
evalStrcmpCommon(C, CE, false, false);
}
void CStringChecker::evalStrncmp(CheckerContext &C, const CallExpr *CE) const {
evalStrcmpCommon(C, CE, true, false);
}
void CStringChecker::evalStrcasecmp(CheckerContext &C,
const CallExpr *CE) const {
evalStrcmpCommon(C, CE, false, true);
}
void CStringChecker::evalStrncasecmp(CheckerContext &C,
const CallExpr *CE) const {
evalStrcmpCommon(C, CE, true, true);
}
void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
bool IsBounded, bool IgnoreCase) const {
CurrentFunctionDescription = "string comparison function";
ProgramStateRef state = C.getState();
const LocationContext *LCtx = C.getLocationContext();
AnyArgExpr Left = {CE->getArg(0), 0};
SVal LeftVal = state->getSVal(Left.Expression, LCtx);
state = checkNonNull(C, state, Left, LeftVal);
if (!state)
return;
AnyArgExpr Right = {CE->getArg(1), 1};
SVal RightVal = state->getSVal(Right.Expression, LCtx);
state = checkNonNull(C, state, Right, RightVal);
if (!state)
return;
SVal LeftLength = getCStringLength(C, state, Left.Expression, LeftVal);
if (LeftLength.isUndef())
return;
SVal RightLength = getCStringLength(C, state, Right.Expression, RightVal);
if (RightLength.isUndef())
return;
DefinedOrUnknownSVal LV = LeftVal.castAs<DefinedOrUnknownSVal>();
DefinedOrUnknownSVal RV = RightVal.castAs<DefinedOrUnknownSVal>();
SValBuilder &svalBuilder = C.getSValBuilder();
DefinedOrUnknownSVal SameBuf = svalBuilder.evalEQ(state, LV, RV);
ProgramStateRef StSameBuf, StNotSameBuf;
std::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf);
if (StSameBuf) {
StSameBuf = StSameBuf->BindExpr(CE, LCtx,
svalBuilder.makeZeroVal(CE->getType()));
C.addTransition(StSameBuf);
if (!StNotSameBuf)
return;
}
assert(StNotSameBuf);
state = StNotSameBuf;
const StringLiteral *LeftStrLiteral =
getCStringLiteral(C, state, Left.Expression, LeftVal);
const StringLiteral *RightStrLiteral =
getCStringLiteral(C, state, Right.Expression, RightVal);
bool canComputeResult = false;
SVal resultVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx,
C.blockCount());
if (LeftStrLiteral && RightStrLiteral) {
StringRef LeftStrRef = LeftStrLiteral->getString();
StringRef RightStrRef = RightStrLiteral->getString();
if (IsBounded) {
const Expr *lenExpr = CE->getArg(2);
SVal lenVal = state->getSVal(lenExpr, LCtx);
if (const llvm::APSInt *len = svalBuilder.getKnownValue(state, lenVal)) {
LeftStrRef = LeftStrRef.substr(0, (size_t)len->getZExtValue());
RightStrRef = RightStrRef.substr(0, (size_t)len->getZExtValue());
canComputeResult = true;
}
} else {
canComputeResult = true;
}
if (canComputeResult) {
size_t s1Term = LeftStrRef.find('\0');
if (s1Term != StringRef::npos)
LeftStrRef = LeftStrRef.substr(0, s1Term);
size_t s2Term = RightStrRef.find('\0');
if (s2Term != StringRef::npos)
RightStrRef = RightStrRef.substr(0, s2Term);
int compareRes = IgnoreCase ? LeftStrRef.compare_insensitive(RightStrRef)
: LeftStrRef.compare(RightStrRef);
if (compareRes == 0) {
resultVal = svalBuilder.makeIntVal(compareRes, CE->getType());
}
else {
DefinedSVal zeroVal = svalBuilder.makeIntVal(0, CE->getType());
BinaryOperatorKind op = (compareRes == 1) ? BO_GT : BO_LT;
SVal compareWithZero =
svalBuilder.evalBinOp(state, op, resultVal, zeroVal,
svalBuilder.getConditionType());
DefinedSVal compareWithZeroVal = compareWithZero.castAs<DefinedSVal>();
state = state->assume(compareWithZeroVal, true);
}
}
}
state = state->BindExpr(CE, LCtx, resultVal);
C.addTransition(state);
}
void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const {
SourceArgExpr SearchStrPtr = {CE->getArg(0), 0};
QualType CharPtrTy = SearchStrPtr.Expression->getType()->getPointeeType();
if (CharPtrTy.isNull() ||
CE->getType().getUnqualifiedType() != CharPtrTy.getUnqualifiedType())
return;
CurrentFunctionDescription = "strsep()";
ProgramStateRef State = C.getState();
const LocationContext *LCtx = C.getLocationContext();
SVal SearchStrVal = State->getSVal(SearchStrPtr.Expression, LCtx);
State = checkNonNull(C, State, SearchStrPtr, SearchStrVal);
if (!State)
return;
AnyArgExpr DelimStr = {CE->getArg(1), 1};
SVal DelimStrVal = State->getSVal(DelimStr.Expression, LCtx);
State = checkNonNull(C, State, DelimStr, DelimStrVal);
if (!State)
return;
SValBuilder &SVB = C.getSValBuilder();
SVal Result;
if (Optional<Loc> SearchStrLoc = SearchStrVal.getAs<Loc>()) {
Result = State->getSVal(*SearchStrLoc, CharPtrTy);
State = InvalidateBuffer(C, State, SearchStrPtr.Expression, Result,
false, nullptr);
State = State->bindLoc(*SearchStrLoc,
SVB.conjureSymbolVal(getTag(),
CE,
LCtx,
CharPtrTy,
C.blockCount()),
LCtx);
} else {
assert(SearchStrVal.isUnknown());
Result = SVB.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
}
State = State->BindExpr(CE, LCtx, Result);
C.addTransition(State);
}
void CStringChecker::evalStdCopy(CheckerContext &C, const CallExpr *CE) const {
evalStdCopyCommon(C, CE);
}
void CStringChecker::evalStdCopyBackward(CheckerContext &C,
const CallExpr *CE) const {
evalStdCopyCommon(C, CE);
}
void CStringChecker::evalStdCopyCommon(CheckerContext &C,
const CallExpr *CE) const {
if (!CE->getArg(2)->getType()->isPointerType())
return;
ProgramStateRef State = C.getState();
const LocationContext *LCtx = C.getLocationContext();
const Expr *Dst = CE->getArg(2);
SVal DstVal = State->getSVal(Dst, LCtx);
State = InvalidateBuffer(C, State, Dst, DstVal, false,
nullptr);
SValBuilder &SVB = C.getSValBuilder();
SVal ResultVal = SVB.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
State = State->BindExpr(CE, LCtx, ResultVal);
C.addTransition(State);
}
void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const {
CurrentFunctionDescription = "memory set function";
DestinationArgExpr Buffer = {CE->getArg(0), 0};
AnyArgExpr CharE = {CE->getArg(1), 1};
SizeArgExpr Size = {CE->getArg(2), 2};
ProgramStateRef State = C.getState();
const LocationContext *LCtx = C.getLocationContext();
SVal SizeVal = C.getSVal(Size.Expression);
QualType SizeTy = Size.Expression->getType();
ProgramStateRef ZeroSize, NonZeroSize;
std::tie(ZeroSize, NonZeroSize) = assumeZero(C, State, SizeVal, SizeTy);
SVal BufferPtrVal = C.getSVal(Buffer.Expression);
if (ZeroSize && !NonZeroSize) {
ZeroSize = ZeroSize->BindExpr(CE, LCtx, BufferPtrVal);
C.addTransition(ZeroSize);
return;
}
State = checkNonNull(C, NonZeroSize, Buffer, BufferPtrVal);
if (!State)
return;
State = CheckBufferAccess(C, State, Buffer, Size, AccessKind::write);
if (!State)
return;
if (!memsetAux(Buffer.Expression, C.getSVal(CharE.Expression),
Size.Expression, C, State))
return;
State = State->BindExpr(CE, LCtx, BufferPtrVal);
C.addTransition(State);
}
void CStringChecker::evalBzero(CheckerContext &C, const CallExpr *CE) const {
CurrentFunctionDescription = "memory clearance function";
DestinationArgExpr Buffer = {CE->getArg(0), 0};
SizeArgExpr Size = {CE->getArg(1), 1};
SVal Zero = C.getSValBuilder().makeZeroVal(C.getASTContext().IntTy);
ProgramStateRef State = C.getState();
SVal SizeVal = C.getSVal(Size.Expression);
QualType SizeTy = Size.Expression->getType();
ProgramStateRef StateZeroSize, StateNonZeroSize;
std::tie(StateZeroSize, StateNonZeroSize) =
assumeZero(C, State, SizeVal, SizeTy);
if (StateZeroSize && !StateNonZeroSize) {
C.addTransition(StateZeroSize);
return;
}
SVal MemVal = C.getSVal(Buffer.Expression);
State = checkNonNull(C, StateNonZeroSize, Buffer, MemVal);
if (!State)
return;
State = CheckBufferAccess(C, State, Buffer, Size, AccessKind::write);
if (!State)
return;
if (!memsetAux(Buffer.Expression, Zero, Size.Expression, C, State))
return;
C.addTransition(State);
}
CStringChecker::FnCheck CStringChecker::identifyCall(const CallEvent &Call,
CheckerContext &C) const {
const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
if (!CE)
return nullptr;
const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
if (!FD)
return nullptr;
if (StdCopy.matches(Call))
return &CStringChecker::evalStdCopy;
if (StdCopyBackward.matches(Call))
return &CStringChecker::evalStdCopyBackward;
for (auto I : CE->arguments()) {
QualType T = I->getType();
if (!T->isIntegralOrEnumerationType() && !T->isPointerType())
return nullptr;
}
const FnCheck *Callback = Callbacks.lookup(Call);
if (Callback)
return *Callback;
return nullptr;
}
bool CStringChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
FnCheck Callback = identifyCall(Call, C);
if (!Callback)
return false;
const auto *CE = cast<CallExpr>(Call.getOriginExpr());
Callback(this, C, CE);
return C.isDifferent();
}
void CStringChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
ProgramStateRef state = C.getState();
for (const auto *I : DS->decls()) {
const VarDecl *D = dyn_cast<VarDecl>(I);
if (!D)
continue;
if (!D->getType()->isArrayType())
continue;
const Expr *Init = D->getInit();
if (!Init)
continue;
if (!isa<StringLiteral>(Init))
continue;
Loc VarLoc = state->getLValue(D, C.getLocationContext());
const MemRegion *MR = VarLoc.getAsRegion();
if (!MR)
continue;
SVal StrVal = C.getSVal(Init);
assert(StrVal.isValid() && "Initializer string is unknown or undefined");
DefinedOrUnknownSVal strLength =
getCStringLength(C, state, Init, StrVal).castAs<DefinedOrUnknownSVal>();
state = state->set<CStringLength>(MR, strLength);
}
C.addTransition(state);
}
ProgramStateRef
CStringChecker::checkRegionChanges(ProgramStateRef state,
const InvalidatedSymbols *,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
const LocationContext *LCtx,
const CallEvent *Call) const {
CStringLengthTy Entries = state->get<CStringLength>();
if (Entries.isEmpty())
return state;
llvm::SmallPtrSet<const MemRegion *, 8> Invalidated;
llvm::SmallPtrSet<const MemRegion *, 32> SuperRegions;
for (ArrayRef<const MemRegion *>::iterator
I = Regions.begin(), E = Regions.end(); I != E; ++I) {
const MemRegion *MR = *I;
Invalidated.insert(MR);
SuperRegions.insert(MR);
while (const SubRegion *SR = dyn_cast<SubRegion>(MR)) {
MR = SR->getSuperRegion();
SuperRegions.insert(MR);
}
}
CStringLengthTy::Factory &F = state->get_context<CStringLength>();
for (CStringLengthTy::iterator I = Entries.begin(),
E = Entries.end(); I != E; ++I) {
const MemRegion *MR = I.getKey();
if (SuperRegions.count(MR)) {
Entries = F.remove(Entries, MR);
continue;
}
const MemRegion *Super = MR;
while (const SubRegion *SR = dyn_cast<SubRegion>(Super)) {
Super = SR->getSuperRegion();
if (Invalidated.count(Super)) {
Entries = F.remove(Entries, MR);
break;
}
}
}
return state->set<CStringLength>(Entries);
}
void CStringChecker::checkLiveSymbols(ProgramStateRef state,
SymbolReaper &SR) const {
CStringLengthTy Entries = state->get<CStringLength>();
for (CStringLengthTy::iterator I = Entries.begin(), E = Entries.end();
I != E; ++I) {
SVal Len = I.getData();
for (SymExpr::symbol_iterator si = Len.symbol_begin(),
se = Len.symbol_end(); si != se; ++si)
SR.markInUse(*si);
}
}
void CStringChecker::checkDeadSymbols(SymbolReaper &SR,
CheckerContext &C) const {
ProgramStateRef state = C.getState();
CStringLengthTy Entries = state->get<CStringLength>();
if (Entries.isEmpty())
return;
CStringLengthTy::Factory &F = state->get_context<CStringLength>();
for (CStringLengthTy::iterator I = Entries.begin(), E = Entries.end();
I != E; ++I) {
SVal Len = I.getData();
if (SymbolRef Sym = Len.getAsSymbol()) {
if (SR.isDead(Sym))
Entries = F.remove(Entries, I.getKey());
}
}
state = state->set<CStringLength>(Entries);
C.addTransition(state);
}
void ento::registerCStringModeling(CheckerManager &Mgr) {
Mgr.registerChecker<CStringChecker>();
}
bool ento::shouldRegisterCStringModeling(const CheckerManager &mgr) {
return true;
}
#define REGISTER_CHECKER(name) \
void ento::register##name(CheckerManager &mgr) { \
CStringChecker *checker = mgr.getChecker<CStringChecker>(); \
checker->Filter.Check##name = true; \
checker->Filter.CheckName##name = mgr.getCurrentCheckerName(); \
} \
\
bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
REGISTER_CHECKER(CStringNullArg)
REGISTER_CHECKER(CStringOutOfBounds)
REGISTER_CHECKER(CStringBufferOverlap)
REGISTER_CHECKER(CStringNotNullTerm)
REGISTER_CHECKER(CStringUninitializedRead)