https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101134
--- Comment #12 from Martin Sebor <msebor at gcc dot gnu.org> --- I don't need to be convinced that it would be nice to be able to differentiate between certain bugs and possible ones. The text of this class of warnings already does differentiate between "may write/read/access" and "writing/reading/accessing" under some conditions: https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=gcc/builtins.c;h=73c12e3bb8c39907b6bd95148f860709dbbf3f50;hb=refs/heads/releases/gcc-11#l4136 This is not among them. In this case the IL that triggers the warning is: <bb 3> [local count: 10145877954]: # size_18 = PHI <512(2), size_13(10)> # prephitmp_54 = PHI <"edFunction.staticBuf(2), newBuf_36(10)> ... <bb 7> [local count: 2825037180]: newBuf_33 = malloc (_51); goto <bb 9>; [100.00%] <bb 8> [local count: 6591753510]: newBuf_35 = realloc (_30, _51); ... <bb 9> [local count: 9416790700]: # newBuf_36 = PHI <newBuf_33(7), newBuf_35(8)> ... <bb 14> [local count: 3449598541]: MEM[(char *)prephitmp_54 + -1B] = 0; <<< -Wstring-overflow prephitmp_54 is set to point to either the beginning of quotedFunction.staticBuf or the dynamically allocated buffer. So prephitmp_54 - 1 is unconditionally invalid and the warning triggers. The warning doesn't consider the predicates leading up to the invalid store: all it uses to make its decision is the statement itself and its arguments. For PHIs, to minimize noise, it triggers only if the statement is invalid for all arguments. This is how most flow-based warnings work in GCC (some like -Warray-bounds don't consider PHIs yet). To do better and either hope to issue a "maybe" kind of a warning or preferably avoid it altogether if the code is unreachable we would need to do what -Wmaybe-uninitialized does (as I said in comment #5) and analyze the predicates. I've been working on extracting the uninitialized predicate analyzer for the last few months. I'm not sure to what extent it will be usable outside the uninitialized pass yet or how well it will work. As we know, -Wmaybe-uninitialized has lots of false positives (and negatives). But even the uninitialized pass issues unconditional warnings for conditional bugs. For instance: int f (void) { int i, j = 0; int *p = i ? &i : &j; return *p; } a.c: In function āfā: a.c:4:14: warning: āiā is used uninitialized [-Wuninitialized] 4 | int *p = i ? &i : &j; | ^ It does that because the first time it runs it's too early to make the distinction, and by the second time it runs to issuse -Wmaybe-uninitialized the uninitialized read has been eliminated. And this is done to strike a reasonable balance between false negatives and positives. So in general, the distinction between the two cases can only be made when it can be discerned from the IL, and the IL doesn't always preserve the conditional nature of the problem statement. So every warning must always be viewed as a "maybe" kind of a warning. It will never be a sure a thing, either at the scope of individual functions, and certainly not with inlining or function cloning.