Why I can easily detect this trivial case of out of bounds array accessing (marked as OK), using clang-tidy, but I can't detect the one marked as KO?
I understand pointer decay in C, but I would suppose that if I'm passing the array as a fixed size type, it could be considered inside the function.
clang-tidy -checks=clang-analyzer main.c -- -std=c99
/* main.c code snipet */
#define CAPACITY 3
typedef float f32_t;
typedef f32_t f32v_t [ CAPACITY ];
static void out_of_bounds_not_detected ( f32v_t v );
int main ( void )
{
f32v_t vec01 = { 0.0f, 0.0f, 0.0f };
f32_t out_of_bounds = vec01 [ CAPACITY ]; /* detected OK */
out_of_bounds_not_detected ( vec01 );
return (int) out_of_bounds;
}
static void out_of_bounds_not_detected ( f32v_t v )
{
v [ CAPACITY ] = 0.0f; /* non-detected KO */
}
I would expect to clang-tidy emit a warning like this inside the function out_of_bounds_not_detected:
warning: array index 3 is past the end of the array
As noted in the answer by alagner, the C language standard (e.g., C99 6.7.5.3p7) specifies that a parameter specified with array type is "adjusted" to a pointer type. However, the standard only requires that the argument supply "at least as many elements as specified by the size expression" (emphasis mine).
Consequently, the code:
does not necessarily violate the language requirements, since a caller could pass an array with four elements. That said, I think it would still be nice for a static analysis tool to report this case, since doing that deliberately would be very poor form at best.
A clear violation would be something like:
but
clang-tidyalso does not report this case.The reason is that
clangdoes not record the array parameter size information in its AST, soclang-tidycannot see it. To observe this, we can dump the AST like so:The key line of the dump is:
which shows the recorded function type as
void (int *). This is allclang-tidycan see, so it cannot report either case.(Well, in this case it can see the definitions of
what_aboutandnot_detectedin the same file, which together comprise a clear violation, but evidently its interprocedural analysis is insufficient to notice the problem that way, which is a little disappointing.)Out of curiosity, and to confirm it isn't stored but omitted in the dump, I decided to look for where in
clangthis information is dropped. The parser itself, inParser::ParseBracketDeclarator(), keeps the array and its size. But the semantic analyzer, inSema::CheckParameter(), adjusts the type (changing the array to a pointer), retaining only the adjusted type in the resulting ParmVarDecl AST object:The variable
Tholds the type as computed by the parser (an array, for the case of an array parameter), butgetAdjustedParameterType()returns a pointer type, and the originalTand its associated size are not retained anywhere.Consequently, for
clang-tidyto report the original case, some changes would be required to theclangparser.