FP c:S3519 Memory access should be explicitly bounded to prevent buffer overflows

Rule: c:S3519
Product: SonarQube Cloud
Type: False Positive

static const char *ini_exts[]= {"ini", 0};

int main()
{
  for (int exts= 0; ini_exts[exts]; exts++)
    printf("%s", ini_exts[exts]);
}

The error as reported on the condition of the for loop:

Access of ‘ini_exts’ at index 2, while it holds only 2 ‘const char *’ elements

Because of the 0 pointer element in ini_exts the exts will never reach 2. So the error of “at index 2” isn’t valid.

Hi Daniel, welcome here!

You refer to the c:S3519 rule in this report, however, in C Sonar would not report this FP. See.

If you are using C++ though, that’s a different story due to the fact that global variables may run constructors from other translation units that would execute code before main would start, potentially clobbering the non-const global variables.

Before I’d go in details why your example have a FP for C++, but not for C, let me start by saying how resolve this.
Just mark the ini_exts const, to allow us to use the values from its initializer when its used in the loop. This should get rid of the FP.

Alternatively, compile your code as C code.


Alright. To explain the intricacies let me change 2 things:

  • For generality, let’s assume the FP is not in main but in a function called foo
  • Let’s assume we have other functions too in the translation unit, call it bar

Why are these two points important:
There is usually only one main function in an entire application, thus we usually need to analyze other functions than main. We usually don’t even see main in an analysis.
Usually global variables are global for the reason to access them from different places.

So these two changes should make the case a bit more realistic.

This means something like this:

static const char *ini_exts[]= {"ini", 0};

void foo() {
  for (int exts= 0; ini_exts[exts]; exts++)
    printf("%s", ini_exts[exts]);
}
void bar() {
  f(ini_exts);
}

If bar was called before foo, then the decayed pointer of ini_exts would be passed to f that may modify ini_exts and overwrite its initial values. So by the time we call foo we can no longer reason about the content of ini_exts, thus the loop may go out of bounds.

Thank you.