2020-03-20 23:20:53 +08:00
|
|
|
// RUN: %clang_analyze_cc1 %s \
|
|
|
|
// RUN: -analyzer-checker=core \
|
|
|
|
// RUN: -analyzer-checker=apiModeling.StdCLibraryFunctions \
|
|
|
|
// RUN: -analyzer-checker=debug.ExprInspection \
|
|
|
|
// RUN: -analyzer-config eagerly-assume=false \
|
|
|
|
// RUN: -triple i686-unknown-linux \
|
|
|
|
// RUN: -verify
|
|
|
|
|
|
|
|
// RUN: %clang_analyze_cc1 %s \
|
|
|
|
// RUN: -analyzer-checker=core \
|
|
|
|
// RUN: -analyzer-checker=apiModeling.StdCLibraryFunctions \
|
|
|
|
// RUN: -analyzer-checker=debug.ExprInspection \
|
|
|
|
// RUN: -analyzer-config eagerly-assume=false \
|
|
|
|
// RUN: -triple x86_64-unknown-linux \
|
|
|
|
// RUN: -verify
|
|
|
|
|
|
|
|
// RUN: %clang_analyze_cc1 %s \
|
|
|
|
// RUN: -analyzer-checker=core \
|
|
|
|
// RUN: -analyzer-checker=apiModeling.StdCLibraryFunctions \
|
|
|
|
// RUN: -analyzer-checker=debug.ExprInspection \
|
|
|
|
// RUN: -analyzer-config eagerly-assume=false \
|
|
|
|
// RUN: -triple armv7-a15-linux \
|
|
|
|
// RUN: -verify
|
|
|
|
|
|
|
|
// RUN: %clang_analyze_cc1 %s \
|
|
|
|
// RUN: -analyzer-checker=core \
|
|
|
|
// RUN: -analyzer-checker=apiModeling.StdCLibraryFunctions \
|
|
|
|
// RUN: -analyzer-checker=debug.ExprInspection \
|
|
|
|
// RUN: -analyzer-config eagerly-assume=false \
|
|
|
|
// RUN: -triple thumbv7-a15-linux \
|
|
|
|
// RUN: -verify
|
2016-10-24 17:41:38 +08:00
|
|
|
|
|
|
|
void clang_analyzer_eval(int);
|
|
|
|
|
|
|
|
int glob;
|
|
|
|
|
|
|
|
typedef struct FILE FILE;
|
|
|
|
#define EOF -1
|
|
|
|
|
|
|
|
int getc(FILE *);
|
|
|
|
void test_getc(FILE *fp) {
|
|
|
|
int x;
|
|
|
|
while ((x = getc(fp)) != EOF) {
|
|
|
|
clang_analyzer_eval(x > 255); // expected-warning{{FALSE}}
|
|
|
|
clang_analyzer_eval(x >= 0); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int fgetc(FILE *);
|
|
|
|
void test_fgets(FILE *fp) {
|
|
|
|
clang_analyzer_eval(fgetc(fp) < 256); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval(fgetc(fp) >= 0); // expected-warning{{UNKNOWN}}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-11-03 03:35:20 +08:00
|
|
|
typedef typeof(sizeof(int)) size_t;
|
2016-10-24 17:41:38 +08:00
|
|
|
typedef signed long ssize_t;
|
|
|
|
ssize_t read(int, void *, size_t);
|
|
|
|
ssize_t write(int, const void *, size_t);
|
|
|
|
void test_read_write(int fd, char *buf) {
|
|
|
|
glob = 1;
|
|
|
|
ssize_t x = write(fd, buf, 10);
|
|
|
|
clang_analyzer_eval(glob); // expected-warning{{UNKNOWN}}
|
|
|
|
if (x >= 0) {
|
|
|
|
clang_analyzer_eval(x <= 10); // expected-warning{{TRUE}}
|
|
|
|
ssize_t y = read(fd, &glob, sizeof(glob));
|
|
|
|
if (y >= 0) {
|
|
|
|
clang_analyzer_eval(y <= sizeof(glob)); // expected-warning{{TRUE}}
|
|
|
|
} else {
|
|
|
|
// -1 overflows on promotion!
|
|
|
|
clang_analyzer_eval(y <= sizeof(glob)); // expected-warning{{FALSE}}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
clang_analyzer_eval(x == -1); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[analyzer] StdLibraryFunctionsChecker: match signature based on FunctionDecl
Summary:
Currently we match the summary signature based on the arguments in the CallExpr.
There are a few problems with this approach.
1) Variadic arguments are handled badly. Consider the below code:
int foo(void *stream, const char *format, ...);
void test_arg_constraint_on_variadic_fun() {
foo(0, "%d%d", 1, 2); // CallExpr
}
Here the call expression holds 4 arguments, whereas the function declaration
has only 2 `ParmVarDecl`s. So there is no way to create a summary that
matches the call expression, because the discrepancy in the number of
arguments causes a mismatch.
2) The call expression does not handle the `restrict` type qualifier.
In C99, fwrite's signature is the following:
size_t fwrite(const void *restrict, size_t, size_t, FILE *restrict);
However, in a call expression, like below, the type of the argument does not
have the restrict qualifier.
void test_fread_fwrite(FILE *fp, int *buf) {
size_t x = fwrite(buf, sizeof(int), 10, fp);
}
This can result in an unmatches signature, so the summary is not applied.
The solution is to match the summary against the referened callee
`FunctionDecl` that we can query from the `CallExpr`.
Further patches will continue with additional refactoring where I am going to
do a lookup during the checker initialization and the signature match will
happen there. That way, we will not check the signature during every call,
rather we will compare only two `FunctionDecl` pointers.
Reviewers: NoQ, Szelethus, gamesh411, baloghadamsoftware
Subscribers: whisperity, xazax.hun, kristof.beyls, szepet, rnkovacs, a.sidorin, mikhail.ramalho, donat.nagy, dkrupp, Charusso, steakhal, danielkiss, ASDenysPetrov, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D77410
2020-04-03 23:01:00 +08:00
|
|
|
size_t fread(void *restrict, size_t, size_t, FILE *);
|
2016-10-24 17:41:38 +08:00
|
|
|
size_t fwrite(const void *restrict, size_t, size_t, FILE *restrict);
|
|
|
|
void test_fread_fwrite(FILE *fp, int *buf) {
|
[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
2020-03-21 00:23:23 +08:00
|
|
|
|
2016-10-24 17:41:38 +08:00
|
|
|
size_t x = fwrite(buf, sizeof(int), 10, fp);
|
|
|
|
clang_analyzer_eval(x <= 10); // expected-warning{{TRUE}}
|
[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
2020-03-21 00:23:23 +08:00
|
|
|
|
2016-10-24 17:41:38 +08:00
|
|
|
size_t y = fread(buf, sizeof(int), 10, fp);
|
|
|
|
clang_analyzer_eval(y <= 10); // expected-warning{{TRUE}}
|
[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
2020-03-21 00:23:23 +08:00
|
|
|
|
2016-10-24 17:41:38 +08:00
|
|
|
size_t z = fwrite(buf, sizeof(int), y, fp);
|
2018-07-16 21:14:46 +08:00
|
|
|
clang_analyzer_eval(z <= y); // expected-warning{{TRUE}}
|
2016-10-24 17:41:38 +08:00
|
|
|
}
|
|
|
|
|
[analyzer] Fix StdLibraryFunctionsChecker NotNull Constraint Check
Summary:
This check was causing a crash in a test case where the 0th argument was
uninitialized ('Assertion `T::isKind(*this)' at line SVals.h:104). This
was happening since the argument was actually undefined, but the castAs
assumes the value is DefinedOrUnknownSVal.
The fix appears to be simply to check for an undefined value and skip
the check allowing the uninitalized value checker to detect the error.
I included a test case that I verified to produce the negative case
prior to the fix, and passes with the fix.
Reviewers: martong, NoQ
Subscribers: xazax.hun, szepet, rnkovacs, a.sidorin, mikhail.ramalho, Szelethus, donat.nagy, Charusso, ASDenysPetrov, baloghadamsoftware, dkrupp, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D77012
2020-03-29 20:48:26 +08:00
|
|
|
void test_fread_uninitialized(void) {
|
|
|
|
void *ptr;
|
|
|
|
size_t sz;
|
|
|
|
size_t nmem;
|
|
|
|
FILE *fp;
|
|
|
|
(void)fread(ptr, sz, nmem, fp); // expected-warning {{1st function call argument is an uninitialized value}}
|
|
|
|
}
|
|
|
|
|
2016-10-24 17:41:38 +08:00
|
|
|
ssize_t getline(char **, size_t *, FILE *);
|
|
|
|
void test_getline(FILE *fp) {
|
|
|
|
char *line = 0;
|
|
|
|
size_t n = 0;
|
|
|
|
ssize_t len;
|
|
|
|
while ((len = getline(&line, &n, fp)) != -1) {
|
|
|
|
clang_analyzer_eval(len == 0); // expected-warning{{FALSE}}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int isascii(int);
|
|
|
|
void test_isascii(int x) {
|
|
|
|
clang_analyzer_eval(isascii(123)); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval(isascii(-1)); // expected-warning{{FALSE}}
|
|
|
|
if (isascii(x)) {
|
|
|
|
clang_analyzer_eval(x < 128); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval(x >= 0); // expected-warning{{TRUE}}
|
|
|
|
} else {
|
|
|
|
if (x > 42)
|
|
|
|
clang_analyzer_eval(x >= 128); // expected-warning{{TRUE}}
|
|
|
|
else
|
|
|
|
clang_analyzer_eval(x < 0); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
glob = 1;
|
|
|
|
isascii('a');
|
|
|
|
clang_analyzer_eval(glob); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
|
|
|
|
int islower(int);
|
|
|
|
void test_islower(int x) {
|
|
|
|
clang_analyzer_eval(islower('x')); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval(islower('X')); // expected-warning{{FALSE}}
|
|
|
|
if (islower(x))
|
|
|
|
clang_analyzer_eval(x < 'a'); // expected-warning{{FALSE}}
|
|
|
|
}
|
|
|
|
|
|
|
|
int getchar(void);
|
|
|
|
void test_getchar() {
|
|
|
|
int x = getchar();
|
|
|
|
if (x == EOF)
|
|
|
|
return;
|
|
|
|
clang_analyzer_eval(x < 0); // expected-warning{{FALSE}}
|
|
|
|
clang_analyzer_eval(x < 256); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
|
|
|
|
int isalpha(int);
|
|
|
|
void test_isalpha() {
|
|
|
|
clang_analyzer_eval(isalpha(']')); // expected-warning{{FALSE}}
|
|
|
|
clang_analyzer_eval(isalpha('Q')); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval(isalpha(128)); // expected-warning{{UNKNOWN}}
|
|
|
|
}
|
|
|
|
|
|
|
|
int isalnum(int);
|
|
|
|
void test_alnum() {
|
|
|
|
clang_analyzer_eval(isalnum('1')); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval(isalnum(')')); // expected-warning{{FALSE}}
|
|
|
|
}
|
|
|
|
|
|
|
|
int isblank(int);
|
|
|
|
void test_isblank() {
|
|
|
|
clang_analyzer_eval(isblank('\t')); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval(isblank(' ')); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval(isblank('\n')); // expected-warning{{FALSE}}
|
|
|
|
}
|
|
|
|
|
|
|
|
int ispunct(int);
|
|
|
|
void test_ispunct(int x) {
|
|
|
|
clang_analyzer_eval(ispunct(' ')); // expected-warning{{FALSE}}
|
|
|
|
clang_analyzer_eval(ispunct(-1)); // expected-warning{{FALSE}}
|
|
|
|
clang_analyzer_eval(ispunct('#')); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval(ispunct('_')); // expected-warning{{TRUE}}
|
|
|
|
if (ispunct(x))
|
|
|
|
clang_analyzer_eval(x < 127); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
|
|
|
|
int isupper(int);
|
|
|
|
void test_isupper(int x) {
|
|
|
|
if (isupper(x))
|
|
|
|
clang_analyzer_eval(x < 'A'); // expected-warning{{FALSE}}
|
|
|
|
}
|
|
|
|
|
|
|
|
int isgraph(int);
|
|
|
|
int isprint(int);
|
|
|
|
void test_isgraph_isprint(int x) {
|
|
|
|
char y = x;
|
|
|
|
if (isgraph(y))
|
|
|
|
clang_analyzer_eval(isprint(x)); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
|
|
|
|
int isdigit(int);
|
|
|
|
void test_mixed_branches(int x) {
|
|
|
|
if (isdigit(x)) {
|
|
|
|
clang_analyzer_eval(isgraph(x)); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval(isblank(x)); // expected-warning{{FALSE}}
|
|
|
|
} else if (isascii(x)) {
|
|
|
|
// isalnum() bifurcates here.
|
|
|
|
clang_analyzer_eval(isalnum(x)); // expected-warning{{TRUE}} // expected-warning{{FALSE}}
|
|
|
|
clang_analyzer_eval(isprint(x)); // expected-warning{{TRUE}} // expected-warning{{FALSE}}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int isspace(int);
|
|
|
|
void test_isspace(int x) {
|
|
|
|
if (!isascii(x))
|
|
|
|
return;
|
|
|
|
char y = x;
|
|
|
|
if (y == ' ')
|
|
|
|
clang_analyzer_eval(isspace(x)); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
|
|
|
|
int isxdigit(int);
|
|
|
|
void test_isxdigit(int x) {
|
|
|
|
if (isxdigit(x) && isupper(x)) {
|
|
|
|
clang_analyzer_eval(x >= 'A'); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval(x <= 'F'); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void test_call_by_pointer() {
|
|
|
|
typedef int (*func)(int);
|
|
|
|
func f = isascii;
|
|
|
|
clang_analyzer_eval(f('A')); // expected-warning{{TRUE}}
|
|
|
|
f = ispunct;
|
|
|
|
clang_analyzer_eval(f('A')); // expected-warning{{FALSE}}
|
|
|
|
}
|