forked from OSchip/llvm-project
[analyzer] StdLibraryFunctionsChecker: Add NotNull Arg Constraint
Reviewers: NoQ, Szelethus, balazske, gamesh411, baloghadamsoftware, steakhal Subscribers: whisperity, xazax.hun, szepet, rnkovacs, a.sidorin, mikhail.ramalho, donat.nagy, dkrupp, Charusso, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D75063
This commit is contained in:
parent
edcfb47ff6
commit
ededa65d55
|
@ -183,6 +183,29 @@ class StdLibraryFunctionsChecker
|
|||
const Summary &Summary) const override;
|
||||
};
|
||||
|
||||
class NotNullConstraint : public ValueConstraint {
|
||||
using ValueConstraint::ValueConstraint;
|
||||
// This variable has a role when we negate the constraint.
|
||||
bool CannotBeNull = true;
|
||||
|
||||
public:
|
||||
ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
|
||||
const Summary &Summary) const override {
|
||||
SVal V = getArgSVal(Call, getArgNo());
|
||||
DefinedOrUnknownSVal L = V.castAs<DefinedOrUnknownSVal>();
|
||||
if (!L.getAs<Loc>())
|
||||
return State;
|
||||
|
||||
return State->assume(L, CannotBeNull);
|
||||
}
|
||||
|
||||
ValueConstraintPtr negate() const override {
|
||||
NotNullConstraint Tmp(*this);
|
||||
Tmp.CannotBeNull = !this->CannotBeNull;
|
||||
return std::make_shared<NotNullConstraint>(Tmp);
|
||||
}
|
||||
};
|
||||
|
||||
/// The complete list of constraints that defines a single branch.
|
||||
typedef std::vector<ValueConstraintPtr> ConstraintSet;
|
||||
|
||||
|
@ -233,9 +256,6 @@ class StdLibraryFunctionsChecker
|
|||
"We should have had no significant void types in the spec");
|
||||
assert(T.isCanonical() &&
|
||||
"We should only have canonical types in the spec");
|
||||
// FIXME: lift this assert (but not the ones above!)
|
||||
assert(T->isIntegralOrEnumerationType() &&
|
||||
"We only support integral ranges in the spec");
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -602,6 +622,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
|
|||
const QualType LongTy = ACtx.LongTy;
|
||||
const QualType LongLongTy = ACtx.LongLongTy;
|
||||
const QualType SizeTy = ACtx.getSizeType();
|
||||
const QualType VoidPtrTy = ACtx.VoidPtrTy; // void *T
|
||||
const QualType ConstVoidPtrTy =
|
||||
ACtx.getPointerType(ACtx.VoidTy.withConst()); // const void *T
|
||||
|
||||
const RangeInt IntMax = BVF.getMaxValue(IntTy).getLimitedValue();
|
||||
const RangeInt LongMax = BVF.getMaxValue(LongTy).getLimitedValue();
|
||||
|
@ -672,6 +695,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
|
|||
return IntRangeVector{std::pair<RangeInt, RangeInt>{v, v}};
|
||||
};
|
||||
auto LessThanOrEq = BO_LE;
|
||||
auto NotNull = [&](ArgNo ArgN) {
|
||||
return std::make_shared<NotNullConstraint>(ArgN);
|
||||
};
|
||||
|
||||
using RetType = QualType;
|
||||
// Templates for summaries that are reused by many functions.
|
||||
|
@ -687,11 +713,20 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
|
|||
ReturnValueCondition(WithinRange, Range(-1, Max))});
|
||||
};
|
||||
auto Fread = [&]() {
|
||||
return Summary(ArgTypes{Irrelevant, Irrelevant, SizeTy, Irrelevant},
|
||||
return Summary(ArgTypes{VoidPtrTy, Irrelevant, SizeTy, Irrelevant},
|
||||
RetType{SizeTy}, NoEvalCall)
|
||||
.Case({
|
||||
ReturnValueCondition(LessThanOrEq, ArgNo(2)),
|
||||
});
|
||||
})
|
||||
.ArgConstraint(NotNull(ArgNo(0)));
|
||||
};
|
||||
auto Fwrite = [&]() {
|
||||
return Summary(ArgTypes{ConstVoidPtrTy, Irrelevant, SizeTy, Irrelevant},
|
||||
RetType{SizeTy}, NoEvalCall)
|
||||
.Case({
|
||||
ReturnValueCondition(LessThanOrEq, ArgNo(2)),
|
||||
})
|
||||
.ArgConstraint(NotNull(ArgNo(0)));
|
||||
};
|
||||
auto Getline = [&](RetType R, RangeInt Max) {
|
||||
return Summary(ArgTypes{Irrelevant, Irrelevant, Irrelevant}, RetType{R},
|
||||
|
@ -893,7 +928,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
|
|||
{"write", Summaries{Read(IntTy, IntMax), Read(LongTy, LongMax),
|
||||
Read(LongLongTy, LongLongMax)}},
|
||||
{"fread", Summaries{Fread()}},
|
||||
{"fwrite", Summaries{Fread()}},
|
||||
{"fwrite", Summaries{Fwrite()}},
|
||||
// getline()-like functions either fail or read at least the delimiter.
|
||||
{"getline", Summaries{Getline(IntTy, IntMax), Getline(LongTy, LongMax),
|
||||
Getline(LongLongTy, LongLongMax)}},
|
||||
|
|
|
@ -59,3 +59,29 @@ void test_alnum_symbolic2(int x) {
|
|||
(void)ret;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct FILE FILE;
|
||||
typedef typeof(sizeof(int)) size_t;
|
||||
size_t fread(void *, size_t, size_t, FILE *);
|
||||
void test_notnull_concrete(FILE *fp) {
|
||||
fread(0, sizeof(int), 10, fp); // \
|
||||
// report-warning{{Function argument constraint is not satisfied}} \
|
||||
// bugpath-warning{{Function argument constraint is not satisfied}} \
|
||||
// bugpath-note{{Function argument constraint is not satisfied}}
|
||||
}
|
||||
void test_notnull_symbolic(FILE *fp, int *buf) {
|
||||
fread(buf, sizeof(int), 10, fp);
|
||||
clang_analyzer_eval(buf != 0); // \
|
||||
// report-warning{{TRUE}} \
|
||||
// bugpath-warning{{TRUE}} \
|
||||
// bugpath-note{{TRUE}} \
|
||||
// bugpath-note{{'buf' is not equal to null}}
|
||||
}
|
||||
void test_notnull_symbolic2(FILE *fp, int *buf) {
|
||||
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}} \
|
||||
// bugpath-warning{{Function argument constraint is not satisfied}} \
|
||||
// bugpath-note{{Function argument constraint is not satisfied}}
|
||||
}
|
||||
|
|
|
@ -78,10 +78,13 @@ void test_read_write(int fd, char *buf) {
|
|||
size_t fread(void *, size_t, size_t, FILE *);
|
||||
size_t fwrite(const void *restrict, size_t, size_t, FILE *restrict);
|
||||
void test_fread_fwrite(FILE *fp, int *buf) {
|
||||
|
||||
size_t x = fwrite(buf, sizeof(int), 10, fp);
|
||||
clang_analyzer_eval(x <= 10); // expected-warning{{TRUE}}
|
||||
|
||||
size_t y = fread(buf, sizeof(int), 10, fp);
|
||||
clang_analyzer_eval(y <= 10); // expected-warning{{TRUE}}
|
||||
|
||||
size_t z = fwrite(buf, sizeof(int), y, fp);
|
||||
clang_analyzer_eval(z <= y); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue