[Bug other/103542] New: bogus -Warray-bounds while index is limited by switch/case

patrickdepinguin at gmail dot com gcc-bugzilla@gcc.gnu.org
Fri Dec 3 15:33:43 GMT 2021


https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103542

            Bug ID: 103542
           Summary: bogus -Warray-bounds while index is limited by
                    switch/case
           Product: gcc
           Version: 11.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: other
          Assignee: unassigned at gcc dot gnu.org
          Reporter: patrickdepinguin at gmail dot com
  Target Milestone: ---

gcc 11.2.0 reports the following on a reduced test case:

$ powerpc-linux-gcc -c array-bounds-fruit.c -O2 -Wall -Werror

array-bounds-fruit.c: In function 'get_default_config.part.0':
array-bounds-fruit.c:69:37: error: array subscript 4 is above array bounds of
'struct fruit_config[4]' [-Werror=array-bounds]
   69 |             do_something(id, &config[id].num_lemons);
      |                               ~~~~~~^~~~
array-bounds-fruit.c:19:28: note: while referencing 'config'
   19 | static struct fruit_config config[4];
      |                            ^~~~~~
cc1: all warnings being treated as errors


Above is for powerpc, but I have the same problem with ARM.

The offending line is inside a switch/case, within the block where 'id' is
tested to be 0, 1, 2, or 3.
gcc/g++ is considering a case where 'id' becomes 4, which is not possible in
this code.

If I make any more changes (even seemingly unrelated changes) to the test case,
the error disappears.

Test code:

#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>

enum {
    ID_0 = 0,
    ID_1 = 1,
    ID_2 = 2,
    ID_3 = 3,
    MAX_IDS,
};
#define MAX_ENTRIES 256

struct fruit_config {
    uint32_t num_apples;
    uint32_t num_lemons;
    uint32_t * lemons;
};
static struct fruit_config config[4];

static uint32_t unrelated_table[MAX_IDS][MAX_ENTRIES];

uint32_t do_something(const uint32_t id, uint32_t * number_of_entries)
{
    uint32_t error = 0;

    switch (id) {
        /* merging these case statements with identical body removes the issue
*/
        case ID_0: {
            *number_of_entries = 0;
            break;
        }
        case ID_1: {
            *number_of_entries = 0;
            break;
        }
        case ID_2: {
            *number_of_entries = 0;
            break;
        }
        case ID_3: {
            *number_of_entries = 0;
            break;
        }
        default: {
            error = 0xff;
            *number_of_entries = 0;
            break;
        }
    }
    return error;
}

struct fruit_config * get_default_config(const uint32_t id)
{
    switch (id) {
        case ID_0:
        case ID_1:
        case ID_2:
        case ID_3:
        {
            uint32_t entry = 0;
            for (entry = 0; entry <config[id].num_apples ; entry++) {
                   unrelated_table[0][0] = 0;
            }

            config[id].num_apples = 0;

            do_something(id, &config[id].num_lemons);

            /* removing following two lines removes the issue, even though
             * the error already occurs above */
            config[id].num_lemons = 0;
            config[id].lemons = NULL;

            /* removing use of error removes the issue */
            extern void foo(uint32_t arg);
            uint32_t error = 0;
            foo(error);

            break;
        }
        default: {
            break;
        }
    }

    return NULL;
}

void func_start(void)
{
    uint32_t i = 0;
    for (i = 0; i < MAX_IDS; i++) {
        get_default_config(i);
    }
}


More information about the Gcc-bugs mailing list