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.

Reply via email to