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.