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

            Bug ID: 112727
           Summary: UBSAN creates GIMPLE path with uninitialized variable
           Product: gcc
           Version: 14.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: sanitizer
          Assignee: unassigned at gcc dot gnu.org
          Reporter: sirl at gcc dot gnu.org
                CC: dodji at gcc dot gnu.org, dvyukov at gcc dot gnu.org,
                    jakub at gcc dot gnu.org, kcc at gcc dot gnu.org, marxin at 
gcc dot gnu.org
  Target Milestone: ---

Hi,

this simple code:

struct Cfg_t
{
        bool bX[8];
};

void Encode(const Cfg_t* pCfg)
{
        unsigned nCfg = 0;
        for (unsigned j = 0; j < 8; j++) {
                nCfg |= ((!pCfg->bX[j])?1:0) << (16 + j);
        }
}

compiled with "gcc-trunk@r14+5779 -c -O2 -fsanitize=undefined
-fno-sanitize=shift-base -fsanitize=bounds-strict -Wuninitialized"

issues this warning:

gcc_wuninitialized_bug.cpp: In function 'void Encode(const Cfg_t*)':
gcc_wuninitialized_bug.cpp:11:38: warning: 'j.1' is used uninitialized
[-Wuninitialized]
   11 |                 nCfg |= ((!pCfg->bX[j])?1:0) << (16 + j);
      |                            ~~~~~~~~~~^
gcc_wuninitialized_bug.cpp:11:38: note: 'j.1' was declared here
   11 |                 nCfg |= ((!pCfg->bX[j])?1:0) << (16 + j);
      |                            ~~~~~~~~~~^


This is a regression starting with gcc-9. gcc-7 and gcc-8 don't create an
invalid path and thus don't warn.

With -fdump-tree-all the .original dump looks like this:

{
  unsigned int nCfg = 0;

  <<cleanup_point   unsigned int nCfg = 0;>>;
  {
    unsigned int j = 0;

    <<cleanup_point     unsigned int j = 0;>>;
    goto <D.3254>;
    <D.3253>:;
    <<cleanup_point <<< Unknown tree: expr_stmt
      (void) (nCfg = TARGET_EXPR <D.3250, if (SAVE_EXPR <j + 16>;, SAVE_EXPR <j
+ 16> > 31;)
        {
          __builtin___ubsan_handle_shift_out_of_bounds (&*.Lubsan_data0, (bool)
pCfg->bX[.UBSAN_BOUNDS (0B, SAVE_EXPR <j>, 8);, SAVE_EXPR <j>;] ? 0 : 1,
(unsigned long) (SAVE_EXPR <j + 16>));
        }
      else
        {
          <<< Unknown tree: void_cst >>>
        }, ((bool) pCfg->bX[.UBSAN_BOUNDS (0B, SAVE_EXPR <j>, 8);, SAVE_EXPR
<j>;] ? 0 : 1) << SAVE_EXPR <j + 16>;>;, nCfg | (unsigned int) D.3250;) >>>>>;
    <<cleanup_point (void) j++ >>;
    <D.3254>:;
    if (j <= 7) goto <D.3253>; else goto <D.3251>;
    <D.3251>:;
  }
}

And then in the .gimple dump:

void Encode (const struct Cfg_t * pCfg)
{
  int D.3250;
  unsigned int D.3255;
  unsigned long iftmp.0;
  unsigned int j.1;
  int iftmp.2;
  unsigned int nCfg;

  nCfg = 0;
  {
    unsigned int j;

    j = 0;
    goto <D.3254>;
    <D.3253>:
    D.3255 = j + 16;
    if (D.3255 > 31) goto <D.3256>; else goto <D.3257>;
    <D.3256>:
    _1 = (unsigned long) D.3255;
    j.1 = j;
    .UBSAN_BOUNDS (0B, j.1, 8);
    _2 = pCfg->bX[j.1];
    if (_2 != 0) goto <D.3260>; else goto <D.3261>;
    <D.3260>:
    iftmp.0 = 0;
    goto <D.3262>;
    <D.3261>:
    iftmp.0 = 1;
    <D.3262>:
    __builtin___ubsan_handle_shift_out_of_bounds (&*.Lubsan_data0, iftmp.0,
_1);
    goto <D.3263>;
    <D.3257>:
    <D.3263>:
    .UBSAN_BOUNDS (0B, j.1, 8);
    _3 = pCfg->bX[j.1];
    if (_3 != 0) goto <D.3265>; else goto <D.3266>;
    <D.3265>:
    iftmp.2 = 0;
    goto <D.3267>;
    <D.3266>:
    iftmp.2 = 1;
    <D.3267>:
    D.3250 = iftmp.2 << D.3255;
    _4 = (unsigned int) D.3250;
    nCfg = nCfg | _4;
    j = j + 1;
    <D.3254>:
    if (j <= 7) goto <D.3253>; else goto <D.3251>;
    <D.3251>:
  }
}

Clearly j.1 is initialized from j only in the D.3256 branch, the D.3257 branch
misses the initialization.

Reply via email to