[clang] ededa65 - [analyzer] StdLibraryFunctionsChecker: Add NotNull Arg Constraint
Gabor Marton via cfe-commits
cfe-commits at lists.llvm.org
Fri Mar 20 09:36:55 PDT 2020
Author: Gabor Marton
Date: 2020-03-20T17:34:29+01:00
New Revision: ededa65d559dac04ae50d3a33610875bb8f1a85e
URL: https://github.com/llvm/llvm-project/commit/ededa65d559dac04ae50d3a33610875bb8f1a85e
DIFF: https://github.com/llvm/llvm-project/commit/ededa65d559dac04ae50d3a33610875bb8f1a85e.diff
LOG: [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
Added:
Modified:
clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
clang/test/Analysis/std-c-library-functions-arg-constraints.c
clang/test/Analysis/std-c-library-functions.c
Removed:
################################################################################
diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
index 2e956cc2bfe7..590dc8b67690 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -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)}},
diff --git a/clang/test/Analysis/std-c-library-functions-arg-constraints.c b/clang/test/Analysis/std-c-library-functions-arg-constraints.c
index 07a9df9526fc..a20b90ad1ccb 100644
--- a/clang/test/Analysis/std-c-library-functions-arg-constraints.c
+++ b/clang/test/Analysis/std-c-library-functions-arg-constraints.c
@@ -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}}
+}
diff --git a/clang/test/Analysis/std-c-library-functions.c b/clang/test/Analysis/std-c-library-functions.c
index 2a090f397714..3f700a7c39a4 100644
--- a/clang/test/Analysis/std-c-library-functions.c
+++ b/clang/test/Analysis/std-c-library-functions.c
@@ -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}}
}
More information about the cfe-commits
mailing list