typedef uint32_t ArgNo;
static const ArgNo Ret;
+ using DescString = SmallString<96>;
/// Returns the string representation of an argument index.
/// E.g.: (1) -> '1st arg', (2) - > '2nd arg'
static SmallString<8> getArgDesc(ArgNo);
+ /// Append textual description of a numeric range [RMin,RMax] to the string
+ /// 'Out'.
+ static void appendInsideRangeDesc(llvm::APSInt RMin, llvm::APSInt RMax,
+ QualType ArgT, BasicValueFactory &BVF,
+ DescString &Out);
+ /// Append textual description of a numeric range out of [RMin,RMax] to the
+ /// string 'Out'.
+ static void appendOutOfRangeDesc(llvm::APSInt RMin, llvm::APSInt RMax,
+ QualType ArgT, BasicValueFactory &BVF,
+ DescString &Out);
class ValueConstraint;
/// (or return value) of a function. Derived classes implement different kind
/// of constraints, e.g range constraints or correlation between two
/// arguments.
+ /// These are used as argument constraints (preconditions) of functions, in
+ /// which case a bug report may be emitted if the constraint is not satisfied.
+ /// Another use is as conditions for summary cases, to create different
+ /// classes of behavior for a function. In this case no description of the
+ /// constraint is needed because the summary cases have an own (not generated)
+ /// description string.
class ValueConstraint {
public:
ValueConstraint(ArgNo ArgN) : ArgN(ArgN) {}
llvm_unreachable("Not implemented");
};
- // Check whether the constraint is malformed or not. It is malformed if the
- // specified argument has a mismatch with the given FunctionDecl (e.g. the
- // arg number is out-of-range of the function's argument list).
+ /// Check whether the constraint is malformed or not. It is malformed if the
+ /// specified argument has a mismatch with the given FunctionDecl (e.g. the
+ /// arg number is out-of-range of the function's argument list).
bool checkValidity(const FunctionDecl *FD) const {
const bool ValidArg = ArgN == Ret || ArgN < FD->getNumParams();
assert(ValidArg && "Arg out of range!");
}
ArgNo getArgNo() const { return ArgN; }
- // Return those arguments that should be tracked when we report a bug. By
- // default it is the argument that is constrained, however, in some special
- // cases we need to track other arguments as well. E.g. a buffer size might
- // be encoded in another argument.
+ /// Return those arguments that should be tracked when we report a bug. By
+ /// default it is the argument that is constrained, however, in some special
+ /// cases we need to track other arguments as well. E.g. a buffer size might
+ /// be encoded in another argument.
virtual std::vector<ArgNo> getArgsToTrack() const { return {ArgN}; }
virtual StringRef getName() const = 0;
- // Represents that in which context do we require a description of the
- // constraint.
+ /// Represents that in which context do we require a description of the
+ /// constraint.
enum class DescriptionKind {
- // The constraint is violated.
+ /// The constraint is violated.
Violation,
- // We assume that the constraint is satisfied.
+ /// We assume that the constraint is satisfied.
Assumption
};
- // Give a description that explains the constraint to the user. Used when
- // the bug is reported.
- virtual std::string describe(DescriptionKind DK, ProgramStateRef State,
+ /// Give a description that explains the constraint to the user. Used when
+ /// a bug is reported or when the constraint is applied and displayed as a
+ /// note.
+ virtual std::string describe(DescriptionKind DK, const CallEvent &Call,
+ ProgramStateRef State,
const Summary &Summary) const {
// There are some descendant classes that are not used as argument
// constraints, e.g. ComparisonConstraint. In that case we can safely
// ignore the implementation of this function.
- llvm_unreachable("Not implemented");
+ llvm_unreachable(
+ "Description not implemented for summary case constraints");
}
protected:
// type (e.g. [0, Socklen_tMax]). If the type is not found, then the range
// is default initialized to be empty.
IntRangeVector Ranges;
+ // A textual description of this constraint for the specific case where the
+ // constraint is used. If empty a generated description will be used.
+ StringRef Description;
public:
StringRef getName() const override { return "Range"; }
- RangeConstraint(ArgNo ArgN, RangeKind Kind, const IntRangeVector &Ranges)
- : ValueConstraint(ArgN), Kind(Kind), Ranges(Ranges) {}
+ RangeConstraint(ArgNo ArgN, RangeKind Kind, const IntRangeVector &Ranges,
+ StringRef Desc = "")
+ : ValueConstraint(ArgN), Kind(Kind), Ranges(Ranges), Description(Desc) {
+ }
- std::string describe(DescriptionKind DK, ProgramStateRef State,
+ std::string describe(DescriptionKind DK, const CallEvent &Call,
+ ProgramStateRef State,
const Summary &Summary) const override;
const IntRangeVector &getRanges() const { return Ranges; }
public:
NotNullConstraint(ArgNo ArgN, bool CannotBeNull = true)
: ValueConstraint(ArgN), CannotBeNull(CannotBeNull) {}
- std::string describe(DescriptionKind DK, ProgramStateRef State,
+ std::string describe(DescriptionKind DK, const CallEvent &Call,
+ ProgramStateRef State,
const Summary &Summary) const override;
StringRef getName() const override { return "NonNull"; }
ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
return Result;
}
- std::string describe(DescriptionKind DK, ProgramStateRef State,
+ std::string describe(DescriptionKind DK, const CallEvent &Call,
+ ProgramStateRef State,
const Summary &Summary) const override;
ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
static SVal getArgSVal(const CallEvent &Call, ArgNo ArgN) {
return ArgN == Ret ? Call.getReturnValue() : Call.getArgSVal(ArgN);
}
+ static std::string getFunctionName(const CallEvent &Call) {
+ assert(Call.getDecl() &&
+ "Call was found by a summary, should have declaration");
+ return cast<NamedDecl>(Call.getDecl())->getNameAsString();
+ }
public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
CheckerContext &C) const {
if (!ChecksEnabled[CK_StdCLibraryFunctionArgsChecker])
return;
- std::string Msg =
- (Twine("Function argument constraint is not satisfied, constraint: ") +
- VC->getName().data())
- .str();
+ assert(Call.getDecl() &&
+ "Function found in summary must have a declaration available");
+ std::string Msg = VC->describe(ValueConstraint::DescriptionKind::Violation,
+ Call, C.getState(), Summary);
+ Msg[0] = toupper(Msg[0]);
if (!BT_InvalidArg)
BT_InvalidArg = std::make_unique<BugType>(
CheckNames[CK_StdCLibraryFunctionArgsChecker],
- "Unsatisfied argument constraints", categories::LogicError);
+ "Function call with invalid argument", categories::LogicError);
auto R = std::make_unique<PathSensitiveBugReport>(*BT_InvalidArg, Msg, N);
- for (ArgNo ArgN : VC->getArgsToTrack())
+ for (ArgNo ArgN : VC->getArgsToTrack()) {
bugreporter::trackExpressionValue(N, Call.getArgExpr(ArgN), *R);
-
- // Highlight the range of the argument that was violated.
- R->addRange(Call.getArgSourceRange(VC->getArgNo()));
-
- // Describe the argument constraint violation in a note.
- std::string Descr = VC->describe(
- ValueConstraint::DescriptionKind::Violation, C.getState(), Summary);
- // Capitalize the first letter b/c we want a full sentence.
- Descr[0] = toupper(Descr[0]);
- R->addNote(Descr, R->getLocation(), Call.getArgSourceRange(VC->getArgNo()));
+ // All tracked arguments are important, highlight them.
+ R->addRange(Call.getArgSourceRange(ArgN));
+ }
C.emitReport(std::move(R));
}
const StdLibraryFunctionsChecker::ArgNo StdLibraryFunctionsChecker::Ret =
std::numeric_limits<ArgNo>::max();
-} // end of anonymous namespace
-
static BasicValueFactory &getBVF(ProgramStateRef State) {
ProgramStateManager &Mgr = State->getStateManager();
SValBuilder &SVB = Mgr.getSValBuilder();
return SVB.getBasicValueFactory();
}
+} // end of anonymous namespace
+
+SmallString<8>
+StdLibraryFunctionsChecker::getArgDesc(StdLibraryFunctionsChecker::ArgNo ArgN) {
+ SmallString<8> Result;
+ Result += std::to_string(ArgN + 1);
+ Result += llvm::getOrdinalSuffix(ArgN + 1);
+ Result += " argument";
+ return Result;
+}
+
+void StdLibraryFunctionsChecker::appendInsideRangeDesc(llvm::APSInt RMin,
+ llvm::APSInt RMax,
+ QualType ArgT,
+ BasicValueFactory &BVF,
+ DescString &Out) {
+ if (RMin.isZero() && RMax.isZero())
+ Out.append("zero");
+ else if (RMin == RMax)
+ RMin.toString(Out);
+ else if (RMin == BVF.getMinValue(ArgT)) {
+ if (RMax == -1)
+ Out.append("< 0");
+ else {
+ Out.append("<= ");
+ RMax.toString(Out);
+ }
+ } else if (RMax == BVF.getMaxValue(ArgT)) {
+ if (RMin.isOne())
+ Out.append("> 0");
+ else {
+ Out.append(">= ");
+ RMin.toString(Out);
+ }
+ } else if (RMin.isNegative() == RMax.isNegative() &&
+ RMin.getLimitedValue() == RMax.getLimitedValue() - 1) {
+ RMin.toString(Out);
+ Out.append(" or ");
+ RMax.toString(Out);
+ } else if (RMin.isNegative() == RMax.isNegative() &&
+ RMin.getLimitedValue() == RMax.getLimitedValue() - 2) {
+ RMin.toString(Out);
+ Out.append(", ");
+ (RMin + 1).toString(Out, 10, RMin.isSigned());
+ Out.append(" or ");
+ RMax.toString(Out);
+ } else {
+ Out.append("between ");
+ RMin.toString(Out);
+ Out.append(" and ");
+ RMax.toString(Out);
+ }
+}
+
+void StdLibraryFunctionsChecker::appendOutOfRangeDesc(llvm::APSInt RMin,
+ llvm::APSInt RMax,
+ QualType ArgT,
+ BasicValueFactory &BVF,
+ DescString &Out) {
+ if (RMin.isZero() && RMax.isZero())
+ Out.append("nonzero");
+ else if (RMin == RMax) {
+ Out.append("not equal to ");
+ RMin.toString(Out);
+ } else if (RMin == BVF.getMinValue(ArgT)) {
+ if (RMax == -1)
+ Out.append(">= 0");
+ else {
+ Out.append("> ");
+ RMax.toString(Out);
+ }
+ } else if (RMax == BVF.getMaxValue(ArgT)) {
+ if (RMin.isOne())
+ Out.append("<= 0");
+ else {
+ Out.append("< ");
+ RMin.toString(Out);
+ }
+ } else if (RMin.isNegative() == RMax.isNegative() &&
+ RMin.getLimitedValue() == RMax.getLimitedValue() - 1) {
+ Out.append("not ");
+ RMin.toString(Out);
+ Out.append(" and not ");
+ RMax.toString(Out);
+ } else {
+ Out.append("not between ");
+ RMin.toString(Out);
+ Out.append(" and ");
+ RMax.toString(Out);
+ }
+}
+
std::string StdLibraryFunctionsChecker::NotNullConstraint::describe(
- DescriptionKind DK, ProgramStateRef State, const Summary &Summary) const {
+ DescriptionKind DK, const CallEvent &Call, ProgramStateRef State,
+ const Summary &Summary) const {
SmallString<48> Result;
const auto Violation = ValueConstraint::DescriptionKind::Violation;
Result += "the ";
Result += getArgDesc(ArgN);
- Result += DK == Violation ? " should not be NULL" : " is not NULL";
+ Result += " to '";
+ Result += getFunctionName(Call);
+ Result += DK == Violation ? "' should not be NULL" : "' is not NULL";
return Result.c_str();
}
std::string StdLibraryFunctionsChecker::RangeConstraint::describe(
- DescriptionKind DK, ProgramStateRef State, const Summary &Summary) const {
+ DescriptionKind DK, const CallEvent &Call, ProgramStateRef State,
+ const Summary &Summary) const {
BasicValueFactory &BVF = getBVF(State);
QualType T = Summary.getArgType(getArgNo());
- SmallString<48> Result;
+ DescString Result;
const auto Violation = ValueConstraint::DescriptionKind::Violation;
Result += "the ";
Result += getArgDesc(ArgN);
- Result += DK == Violation ? " should be " : " is ";
-
- // Range kind as a string.
- Kind == OutOfRange ? Result += "out of" : Result += "within";
-
- // Get the range values as a string.
- Result += " the range ";
- if (Ranges.size() > 1)
- Result += "[";
- unsigned I = Ranges.size();
- for (const std::pair<RangeInt, RangeInt> &R : Ranges) {
- Result += "[";
- const llvm::APSInt &Min = BVF.getValue(R.first, T);
- const llvm::APSInt &Max = BVF.getValue(R.second, T);
- Min.toString(Result);
- Result += ", ";
- Max.toString(Result);
- Result += "]";
- if (--I > 0)
- Result += ", ";
+ Result += " to '";
+ Result += getFunctionName(Call);
+ Result += DK == Violation ? "' should be " : "' is ";
+ if (!Description.empty()) {
+ Result += Description;
+ } else {
+ unsigned I = Ranges.size();
+ if (Kind == WithinRange) {
+ for (const std::pair<RangeInt, RangeInt> &R : Ranges) {
+ appendInsideRangeDesc(BVF.getValue(R.first, T),
+ BVF.getValue(R.second, T), T, BVF, Result);
+ if (--I > 0)
+ Result += " or ";
+ }
+ } else {
+ for (const std::pair<RangeInt, RangeInt> &R : Ranges) {
+ appendOutOfRangeDesc(BVF.getValue(R.first, T),
+ BVF.getValue(R.second, T), T, BVF, Result);
+ if (--I > 0)
+ Result += " and ";
+ }
+ }
}
- if (Ranges.size() > 1)
- Result += "]";
return Result.c_str();
}
-SmallString<8>
-StdLibraryFunctionsChecker::getArgDesc(StdLibraryFunctionsChecker::ArgNo ArgN) {
- SmallString<8> Result;
- Result += std::to_string(ArgN + 1);
- Result += llvm::getOrdinalSuffix(ArgN + 1);
- Result += " argument";
- return Result;
-}
-
std::string StdLibraryFunctionsChecker::BufferSizeConstraint::describe(
- DescriptionKind DK, ProgramStateRef State, const Summary &Summary) const {
+ DescriptionKind DK, const CallEvent &Call, ProgramStateRef State,
+ const Summary &Summary) const {
SmallString<96> Result;
const auto Violation = ValueConstraint::DescriptionKind::Violation;
Result += "the size of the ";
Result += getArgDesc(ArgN);
- Result += DK == Violation ? " should be " : " is ";
- Result += "equal to or greater than the value of ";
+ Result += " to '";
+ Result += getFunctionName(Call);
+ Result += DK == Violation ? "' should be " : "' is ";
+ Result += "equal to or greater than ";
if (ConcreteSize) {
ConcreteSize->toString(Result);
} else if (SizeArgN) {
- Result += "the ";
+ Result += "the value of the ";
Result += getArgDesc(*SizeArgN);
if (SizeMultiplierArgN) {
Result += " times the ";
SmallString<64> Msg;
Msg += "Assuming ";
Msg += Constraint->describe(ValueConstraint::DescriptionKind::Assumption,
- NewState, Summary);
+ Call, NewState, Summary);
const auto ArgSVal = Call.getArgSVal(Constraint->getArgNo());
NewNode = C.addTransition(
NewState, NewNode,
} addToFunctionSummaryMap(ACtx, FunctionSummaryMap, DisplayLoadedSummaries);
// Below are helpers functions to create the summaries.
- auto ArgumentCondition = [](ArgNo ArgN, RangeKind Kind,
- IntRangeVector Ranges) {
- return std::make_shared<RangeConstraint>(ArgN, Kind, Ranges);
+ auto ArgumentCondition = [](ArgNo ArgN, RangeKind Kind, IntRangeVector Ranges,
+ StringRef Desc = "") {
+ return std::make_shared<RangeConstraint>(ArgN, Kind, Ranges, Desc);
};
auto BufferSize = [](auto... Args) {
return std::make_shared<BufferSizeConstraint>(Args...);
{{'0', '9'}, {'A', 'Z'}, {'a', 'z'}, {128, UCharRangeMax}}),
ReturnValueCondition(WithinRange, SingleValue(0))},
ErrnoIrrelevant, "Assuming the character is non-alphanumeric")
- .ArgConstraint(ArgumentCondition(
- 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})));
+ .ArgConstraint(ArgumentCondition(0U, WithinRange,
+ {{EOFv, EOFv}, {0, UCharRangeMax}},
+ "an unsigned char value or EOF")));
addToFunctionSummaryMap(
"isalpha", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
addToFunctionSummaryMap(
"toupper", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
- .ArgConstraint(ArgumentCondition(
- 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})));
+ .ArgConstraint(ArgumentCondition(0U, WithinRange,
+ {{EOFv, EOFv}, {0, UCharRangeMax}},
+ "an unsigned char value or EOF")));
addToFunctionSummaryMap(
"tolower", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
- .ArgConstraint(ArgumentCondition(
- 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})));
+ .ArgConstraint(ArgumentCondition(0U, WithinRange,
+ {{EOFv, EOFv}, {0, UCharRangeMax}},
+ "an unsigned char value or EOF")));
addToFunctionSummaryMap(
"toascii", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
- .ArgConstraint(ArgumentCondition(
- 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})));
+ .ArgConstraint(ArgumentCondition(0U, WithinRange,
+ {{EOFv, EOFv}, {0, UCharRangeMax}},
+ "an unsigned char value or EOF")));
// The getc() family of functions that returns either a char or an EOF.
addToFunctionSummaryMap(
// Functions for testing.
if (ChecksEnabled[CK_StdCLibraryFunctionsTesterChecker]) {
+ const RangeInt IntMin = BVF.getMinValue(IntTy).getLimitedValue();
+
addToFunctionSummaryMap(
"__not_null", Signature(ArgTypes{IntPtrTy}, RetType{IntTy}),
Summary(EvalCallAsPure).ArgConstraint(NotNull(ArgNo(0))));
- // Test range values.
+ // Test inside range constraints.
addToFunctionSummaryMap(
"__single_val_0", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
"__range_1_2", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
.ArgConstraint(ArgumentCondition(0U, WithinRange, Range(1, 2))));
- addToFunctionSummaryMap("__range_1_2__4_5",
+ addToFunctionSummaryMap(
+ "__range_m1_1", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, WithinRange, Range(-1, 1))));
+ addToFunctionSummaryMap(
+ "__range_m2_m1", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, WithinRange, Range(-2, -1))));
+ addToFunctionSummaryMap(
+ "__range_m10_10", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, WithinRange, Range(-10, 10))));
+ addToFunctionSummaryMap("__range_m1_inf",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, WithinRange, Range(-1, IntMax))));
+ addToFunctionSummaryMap("__range_0_inf",
Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
.ArgConstraint(ArgumentCondition(
- 0U, WithinRange, Range({1, 2}, {4, 5}))));
+ 0U, WithinRange, Range(0, IntMax))));
+ addToFunctionSummaryMap("__range_1_inf",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, WithinRange, Range(1, IntMax))));
+ addToFunctionSummaryMap("__range_minf_m1",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, WithinRange, Range(IntMin, -1))));
+ addToFunctionSummaryMap("__range_minf_0",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, WithinRange, Range(IntMin, 0))));
+ addToFunctionSummaryMap("__range_minf_1",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, WithinRange, Range(IntMin, 1))));
+ addToFunctionSummaryMap("__range_1_2__4_6",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, WithinRange, Range({1, 2}, {4, 6}))));
+ addToFunctionSummaryMap(
+ "__range_1_2__4_inf", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, WithinRange,
+ Range({1, 2}, {4, IntMax}))));
+
+ // Test out of range constraints.
+ addToFunctionSummaryMap(
+ "__single_val_out_0", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, OutOfRange, SingleValue(0))));
+ addToFunctionSummaryMap(
+ "__single_val_out_1", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, OutOfRange, SingleValue(1))));
+ addToFunctionSummaryMap(
+ "__range_out_1_2", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, OutOfRange, Range(1, 2))));
+ addToFunctionSummaryMap(
+ "__range_out_m1_1", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, OutOfRange, Range(-1, 1))));
+ addToFunctionSummaryMap(
+ "__range_out_m2_m1", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, OutOfRange, Range(-2, -1))));
+ addToFunctionSummaryMap(
+ "__range_out_m10_10", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, OutOfRange, Range(-10, 10))));
+ addToFunctionSummaryMap("__range_out_m1_inf",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, OutOfRange, Range(-1, IntMax))));
+ addToFunctionSummaryMap("__range_out_0_inf",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, OutOfRange, Range(0, IntMax))));
+ addToFunctionSummaryMap("__range_out_1_inf",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, OutOfRange, Range(1, IntMax))));
+ addToFunctionSummaryMap("__range_out_minf_m1",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, OutOfRange, Range(IntMin, -1))));
+ addToFunctionSummaryMap("__range_out_minf_0",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, OutOfRange, Range(IntMin, 0))));
+ addToFunctionSummaryMap("__range_out_minf_1",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, OutOfRange, Range(IntMin, 1))));
+ addToFunctionSummaryMap("__range_out_1_2__4_6",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, OutOfRange, Range({1, 2}, {4, 6}))));
+ addToFunctionSummaryMap(
+ "__range_out_1_2__4_inf", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(
+ ArgumentCondition(0U, OutOfRange, Range({1, 2}, {4, IntMax}))));
// Test range kind.
addToFunctionSummaryMap(
int __not_null(int *);
void test_not_null(int *x) {
__not_null(nullptr); // \
- // expected-note{{The 1st argument should not be NULL}} \
- // expected-warning{{}}
+ // expected-warning{{The 1st argument to '__not_null' should not be NULL}}
}
// Check the BufferSizeConstraint violation notes.
case 1: {
char buf[9];
__buf_size_arg_constraint_concrete(buf); // \
- // expected-note{{The size of the 1st argument should be equal to or greater than the value of 10}} \
- // expected-warning{{}}
+ // expected-warning{{The size of the 1st argument to '__buf_size_arg_constraint_concrete' should be equal to or greater than 10}}
break;
}
case 2: {
char buf[3];
__buf_size_arg_constraint(buf, 4); // \
- // expected-note{{The size of the 1st argument should be equal to or greater than the value of the 2nd arg}} \
- // expected-warning{{}}
+ // expected-warning{{The size of the 1st argument to '__buf_size_arg_constraint' should be equal to or greater than the value of the 2nd argument}}
break;
}
case 3: {
char buf[3];
__buf_size_arg_constraint_mul(buf, 4, 2); // \
- // expected-note{{The size of the 1st argument should be equal to or greater than the value of the 2nd argument times the 3rd argument}} \
- // expected-warning{{}}
+ // expected-warning{{The size of the 1st argument to '__buf_size_arg_constraint_mul' should be equal to or greater than the value of the 2nd argument times the 3rd argument}}
break;
}
}
}
// Check the RangeConstraint violation notes.
-int __single_val_1(int); // [1, 1]
-int __range_1_2(int); // [1, 2]
-int __range_1_2__4_5(int); // [1, 2], [4, 5]
-void test_range(int x) {
- __single_val_1(2); // \
- // expected-note{{The 1st argument should be within the range [1, 1]}} \
- // expected-warning{{}}
-}
-// Do more specific check against the range strings.
+
+int __single_val_0(int); // [0, 0]
+int __single_val_1(int); // [1, 1]
+int __range_1_2(int); // [1, 2]
+int __range_m1_1(int); // [-1, 1]
+int __range_m2_m1(int); // [-2, -1]
+int __range_m10_10(int); // [-10, 10]
+int __range_m1_inf(int); // [-1, inf]
+int __range_0_inf(int); // [0, inf]
+int __range_1_inf(int); // [1, inf]
+int __range_minf_m1(int); // [-inf, -1]
+int __range_minf_0(int); // [-inf, 0]
+int __range_minf_1(int); // [-inf, 1]
+int __range_1_2__4_6(int); // [1, 2], [4, 6]
+int __range_1_2__4_inf(int); // [1, 2], [4, inf]
+
+int __single_val_out_0(int); // [0, 0]
+int __single_val_out_1(int); // [1, 1]
+int __range_out_1_2(int); // [1, 2]
+int __range_out_m1_1(int); // [-1, 1]
+int __range_out_m2_m1(int); // [-2, -1]
+int __range_out_m10_10(int); // [-10, 10]
+int __range_out_m1_inf(int); // [-1, inf]
+int __range_out_0_inf(int); // [0, inf]
+int __range_out_1_inf(int); // [1, inf]
+int __range_out_minf_m1(int); // [-inf, -1]
+int __range_out_minf_0(int); // [-inf, 0]
+int __range_out_minf_1(int); // [-inf, 1]
+int __range_out_1_2__4_6(int); // [1, 2], [4, 6]
+int __range_out_1_2__4_inf(int); // [1, 2], [4, inf]
+
void test_range_values(int x) {
switch (x) {
- case 1:
- __single_val_1(2); // expected-note{{[1, 1]}} \
- // expected-warning{{}}
- break;
- case 2:
- __range_1_2(3); // expected-note{{[1, 2]}} \
- // expected-warning{{}}
- break;
- case 3:
- __range_1_2__4_5(3); // expected-note{{[[1, 2], [4, 5]]}} \
- // expected-warning{{}}
- break;
+ case 0:
+ __single_val_0(1); // expected-warning{{should be zero}}
+ break;
+ case 1:
+ __single_val_1(2); // expected-warning{{should be 1}}
+ break;
+ case 2:
+ __range_1_2(3); // expected-warning{{should be 1 or 2}}
+ break;
+ case 3:
+ __range_m1_1(3); // expected-warning{{should be between -1 and 1}}
+ break;
+ case 4:
+ __range_m2_m1(1); // expected-warning{{should be -2 or -1}}
+ break;
+ case 5:
+ __range_m10_10(11); // expected-warning{{should be between -10 and 10}}
+ break;
+ case 6:
+ __range_m10_10(-11); // expected-warning{{should be between -10 and 10}}
+ break;
+ case 7:
+ __range_1_2__4_6(3); // expected-warning{{should be 1 or 2 or 4, 5 or 6}}
+ break;
+ case 8:
+ __range_1_2__4_inf(3); // expected-warning{{should be 1 or 2 or >= 4}}
+ break;
}
}
-// Do more specific check against the range kinds.
-int __within(int); // [1, 1]
-int __out_of(int); // [1, 1]
-void test_range_kind(int x) {
+
+void test_range_values_inf(int x) {
switch (x) {
- case 1:
- __within(2); // expected-note{{within}} \
- // expected-warning{{}}
- break;
- case 2:
- __out_of(1); // expected-note{{out of}} \
- // expected-warning{{}}
- break;
+ case 1:
+ __range_m1_inf(-2); // expected-warning{{should be >= -1}}
+ break;
+ case 2:
+ __range_0_inf(-1); // expected-warning{{should be >= 0}}
+ break;
+ case 3:
+ __range_1_inf(0); // expected-warning{{should be > 0}}
+ break;
+ case 4:
+ __range_minf_m1(0); // expected-warning{{should be < 0}}
+ break;
+ case 5:
+ __range_minf_0(1); // expected-warning{{should be <= 0}}
+ break;
+ case 6:
+ __range_minf_1(2); // expected-warning{{should be <= 1}}
+ break;
}
}
+void test_range_values_out(int x) {
+ switch (x) {
+ case 0:
+ __single_val_out_0(0); // expected-warning{{should be nonzero}}
+ break;
+ case 1:
+ __single_val_out_1(1); // expected-warning{{should be not equal to 1}}
+ break;
+ case 2:
+ __range_out_1_2(2); // expected-warning{{should be not 1 and not 2}}
+ break;
+ case 3:
+ __range_out_m1_1(-1); // expected-warning{{should be not between -1 and 1}}
+ break;
+ case 4:
+ __range_out_m2_m1(-2); // expected-warning{{should be not -2 and not -1}}
+ break;
+ case 5:
+ __range_out_m10_10(0); // expected-warning{{should be not between -10 and 10}}
+ break;
+ case 6:
+ __range_out_1_2__4_6(1); // expected-warning{{should be not 1 and not 2 and not between 4 and 6}}
+ break;
+ case 7:
+ __range_out_1_2__4_inf(4); // expected-warning{{should be not 1 and not 2 and < 4}}
+ break;
+ }
+}
+
+void test_range_values_out_inf(int x) {
+ switch (x) {
+ case 1:
+ __range_out_minf_m1(-1); // expected-warning{{should be >= 0}}
+ break;
+ case 2:
+ __range_out_minf_0(0); // expected-warning{{should be > 0}}
+ break;
+ case 3:
+ __range_out_minf_1(1); // expected-warning{{should be > 1}}
+ break;
+ case 4:
+ __range_out_m1_inf(-1); // expected-warning{{should be < -1}}
+ break;
+ case 5:
+ __range_out_0_inf(0); // expected-warning{{should be < 0}}
+ break;
+ case 6:
+ __range_out_1_inf(1); // expected-warning{{should be <= 0}}
+ break;
+ }
+}
void test_alnum_concrete(int v) {
int ret = isalnum(256); // \
- // report-warning{{Function argument constraint is not satisfied}} \
- // report-note{{}} \
- // bugpath-warning{{Function argument constraint is not satisfied}} \
- // bugpath-note{{}} \
- // bugpath-note{{Function argument constraint is not satisfied}}
+ // report-warning{{The 1st argument to 'isalnum' should be an unsigned char value or EOF}} \
+ // bugpath-warning{{The 1st argument to 'isalnum' should be an unsigned char value or EOF}} \
+ // bugpath-note{{The 1st argument to 'isalnum' should be an unsigned char value or EOF}}
(void)ret;
}
// bugpath-note{{Taking true branch}}
int ret = isalnum(x); // \
- // report-warning{{Function argument constraint is not satisfied}} \
- // report-note{{}} \
- // bugpath-warning{{Function argument constraint is not satisfied}} \
- // bugpath-note{{}} \
- // bugpath-note{{Function argument constraint is not satisfied}}
+ // report-warning{{The 1st argument to 'isalnum' should be an unsigned char value or EOF}} \
+ // bugpath-warning{{The 1st argument to 'isalnum' should be an unsigned char value or EOF}} \
+ // bugpath-note{{The 1st argument to 'isalnum' should be an unsigned char value or EOF}}
(void)ret;
}
void test_toupper_concrete(int v) {
int ret = toupper(256); // \
- // report-warning{{Function argument constraint is not satisfied}} \
- // report-note{{}} \
- // bugpath-warning{{Function argument constraint is not satisfied}} \
- // bugpath-note{{}} \
- // bugpath-note{{Function argument constraint is not satisfied}}
+ // report-warning{{The 1st argument to 'toupper' should be an unsigned char value or EOF}} \
+ // bugpath-warning{{The 1st argument to 'toupper' should be an unsigned char value or EOF}} \
+ // bugpath-note{{The 1st argument to 'toupper' should be an unsigned char value or EOF}}
(void)ret;
}
// bugpath-note{{Taking true branch}}
int ret = toupper(x); // \
- // report-warning{{Function argument constraint is not satisfied}} \
- // report-note{{}} \
- // bugpath-warning{{Function argument constraint is not satisfied}} \
- // bugpath-note{{}} \
- // bugpath-note{{Function argument constraint is not satisfied}}
+ // report-warning{{The 1st argument to 'toupper' should be an unsigned char value or EOF}} \
+ // bugpath-warning{{The 1st argument to 'toupper' should be an unsigned char value or EOF}} \
+ // bugpath-note{{The 1st argument to 'toupper' should be an unsigned char value or EOF}}
(void)ret;
}
void test_tolower_concrete(int v) {
int ret = tolower(256); // \
- // report-warning{{Function argument constraint is not satisfied}} \
- // report-note{{}} \
- // bugpath-warning{{Function argument constraint is not satisfied}} \
- // bugpath-note{{}} \
- // bugpath-note{{Function argument constraint is not satisfied}}
+ // report-warning{{The 1st argument to 'tolower' should be an unsigned char value or EOF}} \
+ // bugpath-warning{{The 1st argument to 'tolower' should be an unsigned char value or EOF}} \
+ // bugpath-note{{The 1st argument to 'tolower' should be an unsigned char value or EOF}}
(void)ret;
}
// bugpath-note{{Taking true branch}}
int ret = tolower(x); // \
- // report-warning{{Function argument constraint is not satisfied}} \
- // report-note{{}} \
- // bugpath-warning{{Function argument constraint is not satisfied}} \
- // bugpath-note{{}} \
- // bugpath-note{{Function argument constraint is not satisfied}}
+ // report-warning{{The 1st argument to 'tolower' should be an unsigned char value or EOF}} \
+ // bugpath-warning{{The 1st argument to 'tolower' should be an unsigned char value or EOF}} \
+ // bugpath-note{{The 1st argument to 'tolower' should be an unsigned char value or EOF}}
(void)ret;
}
void test_toascii_concrete(int v) {
int ret = toascii(256); // \
- // report-warning{{Function argument constraint is not satisfied}} \
- // report-note{{}} \
- // bugpath-warning{{Function argument constraint is not satisfied}} \
- // bugpath-note{{}} \
- // bugpath-note{{Function argument constraint is not satisfied}}
+ // report-warning{{The 1st argument to 'toascii' should be an unsigned char value or EOF}} \
+ // bugpath-warning{{The 1st argument to 'toascii' should be an unsigned char value or EOF}} \
+ // bugpath-note{{The 1st argument to 'toascii' should be an unsigned char value or EOF}}
(void)ret;
}
// bugpath-note{{Taking true branch}}
int ret = toascii(x); // \
- // report-warning{{Function argument constraint is not satisfied}} \
- // report-note{{}} \
- // bugpath-warning{{Function argument constraint is not satisfied}} \
- // bugpath-note{{}} \
- // bugpath-note{{Function argument constraint is not satisfied}}
+ // report-warning{{The 1st argument to 'toascii' should be an unsigned char value or EOF}} \
+ // bugpath-warning{{The 1st argument to 'toascii' should be an unsigned char value or EOF}} \
+ // bugpath-note{{The 1st argument to 'toascii' should be an unsigned char value or EOF}}
(void)ret;
}
size_t fread(void *restrict, size_t, size_t, FILE *restrict);
void test_notnull_concrete(FILE *fp) {
fread(0, sizeof(int), 10, fp); // \
- // report-warning{{Function argument constraint is not satisfied}} \
- // report-note{{}} \
- // bugpath-warning{{Function argument constraint is not satisfied}} \
- // bugpath-note{{}} \
- // bugpath-note{{Function argument constraint is not satisfied}}
+ // report-warning{{The 1st argument to 'fread' should not be NULL}} \
+ // bugpath-warning{{The 1st argument to 'fread' should not be NULL}} \
+ // bugpath-note{{The 1st argument to 'fread' should not be NULL}}
}
void test_notnull_symbolic(FILE *fp, int *buf) {
fread(buf, sizeof(int), 10, fp);
if (!buf) // bugpath-note{{Assuming 'buf' is null}} \
// bugpath-note{{Taking true branch}}
fread(buf, sizeof(int), 10, fp); // \
- // report-warning{{Function argument constraint is not satisfied}} \
- // report-note{{}} \
- // bugpath-warning{{Function argument constraint is not satisfied}} \
- // bugpath-note{{}} \
- // bugpath-note{{Function argument constraint is not satisfied}}
+ // report-warning{{The 1st argument to 'fread' should not be NULL}} \
+ // bugpath-warning{{The 1st argument to 'fread' should not be NULL}} \
+ // bugpath-note{{The 1st argument to 'fread' should not be NULL}}
}
void test_no_node_after_bug(FILE *fp, size_t size, size_t n, void *buf) {
if (fp) // \
// bugpath-note{{Taking false branch}}
return;
size_t ret = fread(buf, size, n, fp); // \
- // report-warning{{Function argument constraint is not satisfied}} \
- // report-note{{}} \
- // bugpath-warning{{Function argument constraint is not satisfied}} \
- // bugpath-note{{}} \
- // bugpath-note{{Function argument constraint is not satisfied}}
+ // report-warning{{The 4th argument to 'fread' should not be NULL}} \
+ // bugpath-warning{{The 4th argument to 'fread' should not be NULL}} \
+ // bugpath-note{{The 4th argument to 'fread' should not be NULL}}
clang_analyzer_warnIfReached(); // not reachable
}
// The 3rd parameter should be the number of elements to read, not
// the size in bytes.
fread(wbuf, size, nitems, file); // \
- // report-warning{{Function argument constraint is not satisfied}} \
- // report-note{{}} \
- // bugpath-warning{{Function argument constraint is not satisfied}} \
- // bugpath-note{{}} \
- // bugpath-note{{Function argument constraint is not satisfied}}
+ // report-warning{{The size of the 1st argument to 'fread' should be equal to or greater than the value of the 2nd argument times the 3rd argument}} \
+ // bugpath-warning{{The size of the 1st argument to 'fread' should be equal to or greater than the value of the 2nd argument times the 3rd argument}} \
+ // bugpath-note{{The size of the 1st argument to 'fread' should be equal to or greater than the value of the 2nd argument times the 3rd argument}}
}
int __two_constrained_args(int, int);
int __variadic(void *stream, const char *format, ...);
void test_arg_constraint_on_variadic_fun(void) {
__variadic(0, "%d%d", 1, 2); // \
- // report-warning{{Function argument constraint is not satisfied}} \
- // report-note{{}} \
- // bugpath-warning{{Function argument constraint is not satisfied}} \
- // bugpath-note{{}} \
- // bugpath-note{{Function argument constraint is not satisfied}}
+ // report-warning{{The 1st argument to '__variadic' should not be NULL}} \
+ // bugpath-warning{{The 1st argument to '__variadic' should not be NULL}} \
+ // bugpath-note{{The 1st argument to '__variadic' should not be NULL}}
}
int __buf_size_arg_constraint(const void *, size_t);
void test_buf_size_concrete(void) {
char buf[3]; // bugpath-note{{'buf' initialized here}}
__buf_size_arg_constraint(buf, 4); // \
- // report-warning{{Function argument constraint is not satisfied}} \
- // report-note{{}} \
- // bugpath-warning{{Function argument constraint is not satisfied}} \
- // bugpath-note{{}} \
- // bugpath-note{{Function argument constraint is not satisfied}}
+ // report-warning{{The size of the 1st argument to '__buf_size_arg_constraint' should be equal to or greater than the value of the 2nd argument}} \
+ // bugpath-warning{{The size of the 1st argument to '__buf_size_arg_constraint' should be equal to or greater than the value of the 2nd argument}} \
+ // bugpath-note{{The size of the 1st argument to '__buf_size_arg_constraint' should be equal to or greater than the value of the 2nd argument}}
}
void test_buf_size_symbolic(int s) {
char buf[3];
void test_buf_size_concrete_with_multiplication(void) {
short buf[3]; // bugpath-note{{'buf' initialized here}}
__buf_size_arg_constraint_mul(buf, 4, sizeof(short)); // \
- // report-warning{{Function argument constraint is not satisfied}} \
- // report-note{{}} \
- // bugpath-warning{{Function argument constraint is not satisfied}} \
- // bugpath-note{{}} \
- // bugpath-note{{Function argument constraint is not satisfied}}
+ // report-warning{{The size of the 1st argument to '__buf_size_arg_constraint_mul' should be equal to or greater than the value of the 2nd argument times the 3rd argument}} \
+ // bugpath-warning{{The size of the 1st argument to '__buf_size_arg_constraint_mul' should be equal to or greater than the value of the 2nd argument times the 3rd argument}} \
+ // bugpath-note{{The size of the 1st argument to '__buf_size_arg_constraint_mul' should be equal to or greater than the value of the 2nd argument times the 3rd argument}}
}
void test_buf_size_symbolic_with_multiplication(size_t s) {
short buf[3];
void test_min_buf_size(void) {
char buf[9];// bugpath-note{{'buf' initialized here}}
__buf_size_arg_constraint_concrete(buf); // \
- // report-warning{{Function argument constraint is not satisfied}} \
- // report-note{{}} \
- // bugpath-warning{{Function argument constraint is not satisfied}} \
- // bugpath-note{{}} \
- // bugpath-note{{Function argument constraint is not satisfied}}
+ // report-warning{{The size of the 1st argument to '__buf_size_arg_constraint_concrete' should be equal to or greater than 10}} \
+ // bugpath-warning{{The size of the 1st argument to '__buf_size_arg_constraint_concrete' should be equal to or greater than 10}} \
+ // bugpath-note{{The size of the 1st argument to '__buf_size_arg_constraint_concrete' should be equal to or greater than 10}}
}