Skip to content

Commit 258c9be

Browse files
committed
[clang][analyzer] Handle special value AT_FDCWD in affected standard functions
Some file and directory related functions have an integer file descriptor argument that can be a valid file descriptor or a special value AT_FDCWD. This value is relatively often used in open source projects and is usually defined as a negative number, and the checker reports false warnings (a valid file descriptor is not negative) if this fix is not included. Reviewed By: steakhal Differential Revision: https://reviews.llvm.org/D149160
1 parent 6c06a07 commit 258c9be

File tree

2 files changed

+50
-16
lines changed

2 files changed

+50
-16
lines changed

clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1392,6 +1392,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
13921392
SValBuilder &SVB = C.getSValBuilder();
13931393
BasicValueFactory &BVF = SVB.getBasicValueFactory();
13941394
const ASTContext &ACtx = BVF.getContext();
1395+
Preprocessor &PP = C.getPreprocessor();
13951396

13961397
// Helper class to lookup a type by its name.
13971398
class LookupType {
@@ -1527,14 +1528,11 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
15271528
const RangeInt UCharRangeMax =
15281529
std::min(BVF.getMaxValue(ACtx.UnsignedCharTy).getLimitedValue(), IntMax);
15291530

1530-
// The platform dependent value of EOF.
1531-
// Try our best to parse this from the Preprocessor, otherwise fallback to -1.
1532-
const auto EOFv = [&C]() -> RangeInt {
1533-
if (const std::optional<int> OptInt =
1534-
tryExpandAsInteger("EOF", C.getPreprocessor()))
1535-
return *OptInt;
1536-
return -1;
1537-
}();
1531+
// Get platform dependent values of some macros.
1532+
// Try our best to parse this from the Preprocessor, otherwise fallback to a
1533+
// default value (what is found in a library header).
1534+
const auto EOFv = tryExpandAsInteger("EOF", PP).value_or(-1);
1535+
const auto AT_FDCWDv = tryExpandAsInteger("AT_FDCWD", PP).value_or(-100);
15381536

15391537
// Auxiliary class to aid adding summaries to the summary map.
15401538
struct AddToFunctionSummaryMap {
@@ -1979,6 +1977,12 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
19791977
ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, IntMax))};
19801978
const auto &ReturnsValidFileDescriptor = ReturnsNonnegative;
19811979

1980+
auto ValidFileDescriptorOrAtFdcwd = [&](ArgNo ArgN) {
1981+
return std::make_shared<RangeConstraint>(
1982+
ArgN, WithinRange, Range({AT_FDCWDv, AT_FDCWDv}, {0, IntMax}),
1983+
"a valid file descriptor or AT_FDCWD");
1984+
};
1985+
19821986
// FILE *fopen(const char *restrict pathname, const char *restrict mode);
19831987
addToFunctionSummaryMap(
19841988
"fopen",
@@ -2130,6 +2134,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
21302134
Summary(NoEvalCall)
21312135
.Case(ReturnsZero, ErrnoMustNotBeChecked)
21322136
.Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
2137+
.ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0)))
21332138
.ArgConstraint(NotNull(ArgNo(1))));
21342139

21352140
// int dup(int fildes);
@@ -2207,7 +2212,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
22072212
.Case(ReturnsZero, ErrnoMustNotBeChecked)
22082213
.Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
22092214
.ArgConstraint(NotNull(ArgNo(0)))
2210-
.ArgConstraint(ArgumentCondition(1, WithinRange, Range(0, IntMax)))
2215+
.ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(1)))
22112216
.ArgConstraint(NotNull(ArgNo(2))));
22122217

22132218
// int lockf(int fd, int cmd, off_t len);
@@ -2318,6 +2323,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
23182323
Summary(NoEvalCall)
23192324
.Case(ReturnsZero, ErrnoMustNotBeChecked)
23202325
.Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
2326+
.ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0)))
23212327
.ArgConstraint(NotNull(ArgNo(1))));
23222328

23232329
std::optional<QualType> Dev_tTy = lookupTy("dev_t");
@@ -2339,6 +2345,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
23392345
Summary(NoEvalCall)
23402346
.Case(ReturnsZero, ErrnoMustNotBeChecked)
23412347
.Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
2348+
.ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0)))
23422349
.ArgConstraint(NotNull(ArgNo(1))));
23432350

23442351
// int chmod(const char *path, mode_t mode);
@@ -2357,7 +2364,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
23572364
Summary(NoEvalCall)
23582365
.Case(ReturnsZero, ErrnoMustNotBeChecked)
23592366
.Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
2360-
.ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
2367+
.ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0)))
23612368
.ArgConstraint(NotNull(ArgNo(1))));
23622369

23632370
// int fchmod(int fildes, mode_t mode);
@@ -2381,7 +2388,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
23812388
Summary(NoEvalCall)
23822389
.Case(ReturnsZero, ErrnoMustNotBeChecked)
23832390
.Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
2384-
.ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
2391+
.ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0)))
23852392
.ArgConstraint(NotNull(ArgNo(1))));
23862393

23872394
// int chown(const char *path, uid_t owner, gid_t group);
@@ -2446,9 +2453,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
24462453
Summary(NoEvalCall)
24472454
.Case(ReturnsZero, ErrnoMustNotBeChecked)
24482455
.Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
2449-
.ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
2456+
.ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0)))
24502457
.ArgConstraint(NotNull(ArgNo(1)))
2451-
.ArgConstraint(ArgumentCondition(2, WithinRange, Range(0, IntMax)))
2458+
.ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(2)))
24522459
.ArgConstraint(NotNull(ArgNo(3))));
24532460

24542461
// int unlink(const char *pathname);
@@ -2466,7 +2473,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
24662473
Summary(NoEvalCall)
24672474
.Case(ReturnsZero, ErrnoMustNotBeChecked)
24682475
.Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
2469-
.ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
2476+
.ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0)))
24702477
.ArgConstraint(NotNull(ArgNo(1))));
24712478

24722479
std::optional<QualType> StructStatTy = lookupTy("stat");
@@ -2515,7 +2522,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
25152522
Summary(NoEvalCall)
25162523
.Case(ReturnsZero, ErrnoMustNotBeChecked)
25172524
.Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
2518-
.ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
2525+
.ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0)))
25192526
.ArgConstraint(NotNull(ArgNo(1)))
25202527
.ArgConstraint(NotNull(ArgNo(2))));
25212528

@@ -2690,7 +2697,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
26902697
ReturnValueCondition(WithinRange, Range(0, Ssize_tMax))},
26912698
ErrnoMustNotBeChecked)
26922699
.Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
2693-
.ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
2700+
.ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0)))
26942701
.ArgConstraint(NotNull(ArgNo(1)))
26952702
.ArgConstraint(NotNull(ArgNo(2)))
26962703
.ArgConstraint(BufferSize(/*Buffer=*/ArgNo(2),
@@ -2707,7 +2714,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
27072714
Summary(NoEvalCall)
27082715
.Case(ReturnsZero, ErrnoMustNotBeChecked)
27092716
.Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
2717+
.ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0)))
27102718
.ArgConstraint(NotNull(ArgNo(1)))
2719+
.ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(2)))
27112720
.ArgConstraint(NotNull(ArgNo(3))));
27122721

27132722
// char *realpath(const char *restrict file_name,

clang/test/Analysis/std-c-library-functions-arg-constraints.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// RUN: -analyzer-checker=core \
44
// RUN: -analyzer-checker=apiModeling.StdCLibraryFunctions \
55
// RUN: -analyzer-checker=alpha.unix.StdCLibraryFunctionArgs \
6+
// RUN: -analyzer-config apiModeling.StdCLibraryFunctions:ModelPOSIX=true \
67
// RUN: -analyzer-checker=debug.StdCLibraryFunctionsTester \
78
// RUN: -analyzer-checker=debug.ExprInspection \
89
// RUN: -triple x86_64-unknown-linux-gnu \
@@ -13,6 +14,7 @@
1314
// RUN: -analyzer-checker=core \
1415
// RUN: -analyzer-checker=apiModeling.StdCLibraryFunctions \
1516
// RUN: -analyzer-checker=alpha.unix.StdCLibraryFunctionArgs \
17+
// RUN: -analyzer-config apiModeling.StdCLibraryFunctions:ModelPOSIX=true \
1618
// RUN: -analyzer-checker=debug.StdCLibraryFunctionsTester \
1719
// RUN: -analyzer-checker=debug.ExprInspection \
1820
// RUN: -triple x86_64-unknown-linux-gnu \
@@ -309,3 +311,26 @@ void test_min_buf_size(void) {
309311
// bugpath-warning{{The 1st argument to '__buf_size_arg_constraint_concrete' is out of the accepted range; It should be a buffer with size equal to or greater than 10}} \
310312
// bugpath-note{{The 1st argument to '__buf_size_arg_constraint_concrete' is out of the accepted range; It should be a buffer with size equal to or greater than 10}}
311313
}
314+
315+
void test_file_fd_at_functions() {
316+
(void)linkat(-22, "from", AT_FDCWD, "to", 0); // \
317+
// report-warning{{The 1st argument to 'linkat' is -22 but should be a valid file descriptor or AT_FDCWD}} \
318+
// bugpath-warning{{The 1st argument to 'linkat' is -22 but should be a valid file descriptor or AT_FDCWD}} \
319+
// bugpath-note{{The 1st argument to 'linkat' is -22 but should be a valid file descriptor or AT_FDCWD}}
320+
321+
// no warning for these functions if the AT_FDCWD value is used
322+
(void)linkat(AT_FDCWD, "from", AT_FDCWD, "to", 0);
323+
(void)faccessat(AT_FDCWD, "path", 0, 0);
324+
(void)symlinkat("oldpath", AT_FDCWD, "newpath");
325+
(void)mkdirat(AT_FDCWD, "path", 0);
326+
(void)mknodat(AT_FDCWD, "path", 0, 0);
327+
(void)fchmodat(AT_FDCWD, "path", 0, 0);
328+
(void)fchownat(AT_FDCWD, "path", 0, 0, 0);
329+
(void)linkat(AT_FDCWD, "oldpath", AT_FDCWD, "newpath", 0);
330+
(void)unlinkat(AT_FDCWD, "newpath", 0);
331+
struct stat St;
332+
(void)fstatat(AT_FDCWD, "newpath", &St, 0);
333+
char Buf[10];
334+
(void)readlinkat(AT_FDCWD, "newpath", Buf, 10);
335+
(void)renameat(AT_FDCWD, "oldpath", AT_FDCWD, "newpath");
336+
}

0 commit comments

Comments
 (0)