https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100543
Bug ID: 100543 Summary: -Wanalyzer-free-of-non-heap false positive / delete false positive due to constructor conditional Product: gcc Version: 11.1.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: analyzer Assignee: dmalcolm at gcc dot gnu.org Reporter: andrew at ishiboo dot com Target Milestone: --- ##### Test case: #include <cstdio> #include <cstdlib> #include <new> struct Assert { [[ noreturn ]] static void noReturn(); }; template <class TYPE> union ObjectBuffer { private: char d_buffer[sizeof(TYPE)]; double d_align; public: char *buffer() { return d_buffer; } TYPE& object() { return *reinterpret_cast<TYPE *>(this); } }; struct Foo { char *d_buf; unsigned d_sz; Foo(char *buf, unsigned sz) : d_buf(buf), d_sz(sz) { #ifdef ANALYZER_FAILURE if (0 == d_buf) Assert::noReturn(); if (0 == d_sz) Assert::noReturn(); #endif } char *allocate(unsigned) { return d_buf; } }; int main(int argc, char **argv) { ObjectBuffer<Foo> objectBuffer; char buf[sizeof(Foo)]; void *ptr = new (objectBuffer.buffer()) Foo(buf, sizeof(buf)); return printf("%p\n", static_cast<Foo *>(ptr)->allocate(1)); } ###### Output: Works just fine: $ g++-11 -fanalyzer -c /tmp/test.cpp Fails: $ g++-11 -DANALYZER_FAILURE -fanalyzer -c /tmp/test.cpp /tmp/test.cpp: In function 'int main(int, char**)': /tmp/test.cpp:36:65: warning: 'delete' of '& objectBuffer.ObjectBuffer<Foo>::d_buffer' which points to memory not on the heap [CWE-590] [-Wanalyzer-free-of-non-heap] 36 | void *ptr = new (objectBuffer.buffer()) Foo(buf, sizeof(buf)); | ^ 'int main(int, char**)': events 1-2 | | 33 | int main(int argc, char **argv) { | | ^~~~ | | | | | (1) entry to 'main' |...... | 36 | void *ptr = new (objectBuffer.buffer()) Foo(buf, sizeof(buf)); | | ~~~~~~~~~~~~~~~~~~~~~ | | | | | (2) calling 'ObjectBuffer<Foo>::buffer' from 'main' | +--> 'char* ObjectBuffer<TYPE>::buffer() [with TYPE = Foo]': events 3-4 | | 15 | char *buffer() { return d_buffer; } | | ^~~~~~ ~~~~~~~~ | | | | | | | (4) pointer is from here | | (3) entry to 'ObjectBuffer<Foo>::buffer' | <------+ | 'int main(int, char**)': events 5-6 | | 36 | void *ptr = new (objectBuffer.buffer()) Foo(buf, sizeof(buf)); | | ~~~~~~~~~~~~~~~~~~~^~ ~ | | | | | | | (6) calling 'Foo::Foo' from 'main' | | (5) returning to 'main' from 'ObjectBuffer<Foo>::buffer' | +--> 'Foo::Foo(char*, unsigned int)': events 7-11 | | 23 | Foo(char *buf, unsigned sz) : d_buf(buf), d_sz(sz) { | | ^~~ | | | | | (7) entry to 'Foo::Foo' | 24 | #ifdef ANALYZER_FAILURE | 25 | if (0 == d_buf) Assert::noReturn(); | | ~~ | | | | | (8) following 'false' branch... | 26 | if (0 == d_sz) Assert::noReturn(); | | ~~ ~~~~ | | | | | | | (9) ...to here | | (10) following 'false' branch... | 27 | #endif | 28 | } | | ~ | | | | | (11) ...to here | <------+ | 'int main(int, char**)': events 12-13 | | 36 | void *ptr = new (objectBuffer.buffer()) Foo(buf, sizeof(buf)); | | ^ | | | | | (12) returning to 'main' from 'Foo::Foo' | | (13) call to 'delete' here | ###### Info: "(13) call to 'delete' here" makes no sense. The entire thing is only triggered by the presence of either conditional on line 25/26. If the conditional branches are removed, it compiles fine.