-
Notifications
You must be signed in to change notification settings - Fork 14.6k
Open
Description
Example code: https://godbolt.org/z/xhbsaYex6
// clang-19 --analyze -Xanalyzer -analyzer-checker="alpha.security,optin.taint" test.c // clang-19 and above
// clang-18 --analyze -Xanalyzer -analyzer-checker="alpha.security" test.c // clang-18 and below
#include <stdio.h>
#include <stdlib.h>
void foo1() {
char buf[20];
if (fgets(buf, sizeof(buf), stdin) == NULL)
return;
int idx = atoi(buf);
buf[idx] = '\0'; // expect-warning
}
void foo2() {
char buf[20];
fgets(buf, sizeof(buf), stdin);
int idx = atoi(buf);
buf[idx] = '\0'; // expect-warning
}
Results:
// clang-19 and above
<source>:17:3: warning: Potential out of bound access to 'buf' with tainted index [alpha.security.ArrayBoundV2]
17 | buf[idx] = '\0'; // expect-warning
| ^~~~~~~~
1 warning generated.
// clang-18 and below
<source>:10:3: warning: Potential out of bound access to 'buf' with tainted index [alpha.security.ArrayBoundV2]
10 | buf[idx] = '\0'; // expect-warning
| ^~~~~~~~
<source>:17:3: warning: Potential out of bound access to 'buf' with tainted index [alpha.security.ArrayBoundV2]
17 | buf[idx] = '\0'; // expect-warning
| ^~~~~~~~
2 warnings generated.
I found that clang-19 version had a false negative in the foo1
test case.
After some debugging, I found there might be problem with the modeling of fgets
in StdLibraryFunctionsChecker
.
llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
Lines 2267 to 2280 in ab51ecc
// char *fgets(char *restrict s, int n, FILE *restrict stream); | |
addToFunctionSummaryMap( | |
"fgets", | |
Signature(ArgTypes{CharPtrRestrictTy, IntTy, FilePtrRestrictTy}, | |
RetType{CharPtrTy}), | |
Summary(NoEvalCall) | |
.Case({ReturnValueCondition(BO_EQ, ArgNo(0))}, | |
ErrnoMustNotBeChecked, GenericSuccessMsg) | |
.Case({IsNull(Ret)}, ErrnoIrrelevant, GenericFailureMsg) | |
.ArgConstraint(NotNull(ArgNo(0))) | |
.ArgConstraint(ArgumentCondition(1, WithinRange, Range(0, IntMax))) | |
.ArgConstraint( | |
BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1))) | |
.ArgConstraint(NotNull(ArgNo(2)))); |
I am not very familiar with function summary modeling. Should we add the following case for the fgets
function here?
.Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg)