Skip to content

[analyzer] False negative in ArrayBoundV2 and TaintPropagation #115410

@z1nke

Description

@z1nke

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.

// 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)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions