clang-tidy: Inconsistent chain of reasoning using

Hello, I’m seeing an inconsistent chain of reasoning for a particular tidy check - core.uninitialized.Assign. This was isolated from a much larger case we found in testing a large code base. I’ve pasted in the particulars below.

I can see that the analyzer is detecting itemlist is uninitialized (step 9). But I would think the analysis should detect that itemlist is initialized in the call to ItemCtor. Indeed, notes 3) and 4) indicate that the path was detected and simulated, but the conditions at steps 3) and 7) are inconsistent with respect to each other. How can ‘i’ be < ‘xxs’ and ‘>=’ xxs’ in the same simulation path analysis?

A really puzzling piece is if I change the prototype for ItemCtor from
void ItemCtor(const Item * const items, int itemlist)
to

void ItemCtor(const Item * items, int itemlist)
I do not see the checker warning.

Has anyone else seen this? Is there something I’m missing about this particular code snippet, or is there perhaps some issue with the way cases are generated by the checker?

Thanks - Jakob

The text output from the simple repro case I have is …

  1. uninit.c:21:11: warning: Assigned value is garbage or undefined
    sum += itemlist[i];
    ^ ~~~~~~~~~~~
  2. uninit.c:16:4: note: Calling ‘ItemCtor’
    ItemCtor(items, itemlist);
    ^~~~~~~~~~~~~~~~~~~~~~~~~
  3. uninit.c:9:20: note: Assuming ‘i’ is >= field ‘xxs’
    for (int i = 0; i < items->xxs; i++) {
    ^~~~~~~~~~~~~~
  4. uninit.c:9:4: note: Loop condition is false. Execution continues on line 9
    for (int i = 0; i < items->xxs; i++) {
    ^
  5. uninit.c:16:4: note: Returning from ‘ItemCtor’
    ItemCtor(items, itemlist);
    ^~~~~~~~~~~~~~~~~~~~~~~~~
  6. uninit.c:20:9: note: ‘i’ initialized to 0
    for (int i = 0; i < items->xxs; i++) {
    ^~~~~
  7. uninit.c:20:20: note: Assuming ‘i’ is < field ‘xxs’
    for (int i = 0; i < items->xxs; i++) {
    ^~~~~~~~~~~~~~
  8. uninit.c:20:4: note: Loop condition is true. Entering loop body
    for (int i = 0; i < items->xxs; i++) {
    ^
  9. uninit.c:21:11: note: Assigned value is garbage or undefined
    sum += itemlist[i];
    ^ ~~~~~~~~~~~
    1 warning generated.

The command …

clang --analyze -Xclang -analyzer-checker=core.uninitialized.Assign -Xclang -analyzer-output=text uninit.c

The simple repro case with line numbers…

1 #include <assert.h>
2
3 #define ITEMS 2
4 typedef struct {
5 int xxs;
6 } Item;
7
8 void ItemCtor(const Item * const items, int itemlist) {
9 for (int i = 0; i < items->xxs; i++) {
10 itemlist[i] = 0;
11 }
12 }
13
14 int work(Item const *items) {
15 int itemlist[ITEMS];
16 ItemCtor(items, itemlist);
17
18 int sum = 0;
19
20 for (int i = 0; i < items->xxs; i++) {
21 sum += itemlist[i];
22 }
23
24 return sum;
25 }

  • a couple of folks with whom we’ve just been talking about these issues.

Had a look, thanks! It’s one of those nasty pointer cast representation bugs:

reg_$1<int Element{SymRegion{reg_$0<const Item * items>},0 S64b,Item}->xxs> : { [-2147483648, 0] }
reg_$2<int SymRegion{reg_$0<const Item * items>}->xxs> : { [1, 2147483647] }

In this case basically the same value has two different representations in the Static Analyzer, so after dereferencing it thinks that these are two different values.

I wanted to clean up this mess for a long time but never had time for it. This time i created to track this problem.

Artem Dergachev via cfe-dev writes:

Had a look, thanks! It's one of those nasty pointer cast representation
bugs:

Hi,

the last few days I've been chasing a false positive in our code base
which I believe is similar to this. I managed to minimize down to:

int hasharray(void **d) {
        void *entries[1];
        entries[0] = d[0];

        char *buf = (char *)entries;
        return buf[1]; // warning: Undefined or garbage value returned to caller
}

clang_analyzer_getExtent of `entries` is 8, whereas for `buf` is it 1.

I find it quite interesting that by changing `entries` to just be a
single pointer rather than an array it works as expected.

Not sure this helps you in any way, but thought it might be good to
share it.

anders