std::vector null pointer dereference from g++ -fanalyzer

Viewed 397

I was experimenting with -fanalyzer on gcc 10, and managed to get a null-pointer dereference reported in std::vector. But I'm not sure if my code has some mistake?

#include <vector>

class Bar
{
public:
    explicit Bar()
    {
    }
    int m_val;
};

int main()
{
    std::vector<Bar> a;
    std::vector<Bar> b(a);
    static_cast<void>(b);
    return 0;
}

It only fails with -O2 (or -O -O1 -O3)

g++-10 -fanalyzer TestVector.cpp -O2
In copy constructor ‘std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = Bar; _Alloc = std::allocator<Bar>]’:                                                                     
cc1plus: warning: dereference of NULL ‘__cur’ [CWE-690] [-Wanalyzer-null-dereference]                                                                                                                       
  ‘std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = Bar; _Alloc = std::allocator<Bar>]’: events 1-2                                                                            
    |                                                                                                                                                                                                       
    |/usr/include/c++/10/bits/stl_vector.h:553:7:                                                                                                                                                           
    |  305 |       { _M_create_storage(__n); }                                                                                                                                                              
    |      |         ~~~~~~~~~~~~~~~~~~~~~~                                                                                                                                                                 
    |      |                          |                                                                                                                                                                     
    |      |                          (2) calling ‘std::_Vector_base<Bar, std::allocator<Bar> >::_M_create_storage’ from ‘std::vector<Bar>::vector’
    |......
    |  553 |       vector(const vector& __x)
    |      |       ^~~~~~
    |      |       |
    |      |       (1) entry to ‘std::vector<Bar>::vector’
    |
    +--> ‘void std::_Vector_base<_Tp, _Alloc>::_M_create_storage(std::size_t) [with _Tp = Bar; _Alloc = std::allocator<Bar>]’: events 3-5
           |
           |  346 |  return __n != 0 ? _Tr::allocate(_M_impl, __n) : pointer();
           |      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           |      |                  |
           |      |                  (4) following ‘false’ branch (when ‘__n == 0’)...
           |......
           |  359 |       _M_create_storage(size_t __n)
           |      |       ^~~~~~~~~~~~~~~~~
           |      |       |
           |      |       (3) entry to ‘std::_Vector_base<Bar, std::allocator<Bar> >::_M_create_storage’
           |  360 |       {
           |  361 |  this->_M_impl._M_start = this->_M_allocate(__n);
           |      |  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           |      |                         |
           |      |                         (5) ...to here
           |
    <------+
    |
  ‘std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = Bar; _Alloc = std::allocator<Bar>]’: events 6-7
    |
    |  305 |       { _M_create_storage(__n); }
    |      |         ~~~~~~~~~~~~~~~~~^~~~~
    |      |                          |
    |      |                          (6) returning to ‘std::vector<Bar>::vector’ from ‘std::_Vector_base<Bar, std::allocator<Bar> >::_M_create_storage’
    |......
    |  558 |    std::__uninitialized_copy_a(__x.begin(), __x.end(),
    |      |    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    |      |                               |
    |      |                               (7) ‘<unknown>’ is NULL
    |  559 |           this->_M_impl._M_start,
    |      |           ~~~~~~~~~~~~~~~~~~~~~~~
    |  560 |           _M_get_Tp_allocator());
    |      |           ~~~~~~~~~~~~~~~~~~~~~~
    |
  ‘std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = Bar; _Alloc = std::allocator<Bar>]’: event 8
    |
    |/usr/include/c++/10/bits/stl_uninitialized.h:90:23:
    |   90 |        for (; __first != __last; ++__first, (void)++__cur)
    |      |               ~~~~~~~~^~~~~~~~~
    |      |                       |
    |      |                       (8) following ‘true’ branch...
    |
  ‘std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = Bar; _Alloc = std::allocator<Bar>]’: event 9
    |
    |/usr/include/c++/10/bits/stl_iterator.h:980:2:
    |  980 |  ++_M_current;
    |      |  ^~
    |      |  |
    |      |  (9) ...to here
    |
  ‘std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = Bar; _Alloc = std::allocator<Bar>]’: event 10
    |
    |cc1plus:
    | (10): dereference of NULL ‘__cur’
    |

g++-10 -fanalyzer TestVector.cpp -O0 compiles fine.

Removing m_val or the constructor from Bar also compile fine.

$ g++-10 --version
g++-10 (Ubuntu 10.1.0-2ubuntu1~18.04) 10.1.0

Live demo

2 Answers

But I'm not sure if my code has some mistake?

Your code is fine.

The warning comes from within the standard library in this case. There are two possible reasons: The standard library implementation has a bug, or the warning is a false positive.

Related