<html>
    <head>
      <base href="https://bugs.llvm.org/">
    </head>
    <body><table border="1" cellspacing="0" cellpadding="8">
        <tr>
          <th>Bug ID</th>
          <td><a class="bz_bug_link 
          bz_status_NEW "
   title="NEW - scan-build reports a false positive nullptr dereference because it apparently incorrectly tracks the value of a class member"
   href="https://bugs.llvm.org/show_bug.cgi?id=39064">39064</a>
          </td>
        </tr>

        <tr>
          <th>Summary</th>
          <td>scan-build reports a false positive nullptr dereference because it apparently incorrectly tracks the value of a class member
          </td>
        </tr>

        <tr>
          <th>Product</th>
          <td>clang
          </td>
        </tr>

        <tr>
          <th>Version</th>
          <td>trunk
          </td>
        </tr>

        <tr>
          <th>Hardware</th>
          <td>PC
          </td>
        </tr>

        <tr>
          <th>OS</th>
          <td>Linux
          </td>
        </tr>

        <tr>
          <th>Status</th>
          <td>NEW
          </td>
        </tr>

        <tr>
          <th>Severity</th>
          <td>enhancement
          </td>
        </tr>

        <tr>
          <th>Priority</th>
          <td>P
          </td>
        </tr>

        <tr>
          <th>Component</th>
          <td>Static Analyzer
          </td>
        </tr>

        <tr>
          <th>Assignee</th>
          <td>dcoughlin@apple.com
          </td>
        </tr>

        <tr>
          <th>Reporter</th>
          <td>thomas.ullmann@mpibpc.mpg.de
          </td>
        </tr>

        <tr>
          <th>CC</th>
          <td>llvm-bugs@lists.llvm.org
          </td>
        </tr></table>
      <p>
        <div>
        <pre>Created <span class=""><a href="attachment.cgi?id=20913" name="attach_20913" title="The full source and output of scan-build including the html-output">attachment 20913</a> <a href="attachment.cgi?id=20913&action=edit" title="The full source and output of scan-build including the html-output">[details]</a></span>
The full source and output of scan-build including the html-output

Scan-build warns for a nullptr dereference which can not
happen because a conditional makes sure that this branch
is never executed.

In the below example program the value of a boolean member
variable is assumed to be true although it is for the sake
of the example even declared const and initialized to false.
As a result, scan-build assumes that a condition that checks
the value of this bool is true, which would result in a nullptr
dereference.

/*---------------------------------------------------------
    a simple example program for debugging scan-build
-----------------------------------------------------------*/

#include <memory>
#include <iostream>

/* ------------------------------------------------------------------
     A stripped-down version of a 3D array for debugging scan-build
   ------------------------------------------------------------------ */

template<class T = size_t, class Allocator = std::allocator<T> >
class IrregArray3D
{
    public:
        //! the data type stored in the array
        typedef T value_type;
        //! the allocator type to allocate value_type**
        typedef typename std::allocator_traits<Allocator>::template
rebind_alloc<value_type**> p2_allocator_type;
        //! the allocator type to allocate value_type*
        typedef typename std::allocator_traits<Allocator>::template
rebind_alloc<value_type*> p_allocator_type;
        //! the allocator type to allocate value_type
        typedef typename std::allocator_traits<Allocator>::template
rebind_alloc<value_type> allocator_type;
        //! the allocator type to allocate value_type*
        typedef typename std::allocator_traits<Allocator>::pointer
allocator_return_ptr_type;
        //! the allocator type to allocate size_type
        typedef typename std::allocator_traits<Allocator>::template
rebind_alloc<size_t> allocator_size_type;
    private:
        //! ending index of dimension 1
        size_t              last1_;
        //! ending index of dimension 2
        size_t              last2_;
        //! ending index of dimension 3
        size_t              last3_;
        //! irreg_ is constant nullptr
        //! an array that would contain information on non-uniform array stripe
lengths
        const size_t*       irreg_;
        //! array storing the the actual data
        value_type           ***arr_;
        //! the allocator_ used to allocate arr_
        p2_allocator_type       p2_allocator_;
        //! the allocator_ used to allocate arr_[i]
        p_allocator_type        p_allocator_;
        //! the allocator_ used to allocate arr_[i][j]
        allocator_type          allocator_;
    public:
        //! this flag is const and set to false in the constructor
        //! but scan-build considers it true
        const bool              isIrreg_;

        IrregArray3D()
            : last1_(-1), last2_(-1), last3_(-1),
              isIrreg_(false), irreg_(nullptr), arr_(nullptr) { }

        /*! \brief   constructor for a regularly shaped data data with
initialization

            \param[in]    x2       last index in dimension 1
            \param[in]    y2       last index in dimension 2
            \param[in]    z2       last index in dimension 3
            \param[in]    ini      initialization value for the data array
elements
         */
        IrregArray3D(const size_t x2,
                     const size_t y2,
                     const size_t z2)
            : IrregArray3D()
        {
            deallocateArray();
            last1_     = x2;
            last2_     = y2;
            last3_     = z2;
            allocateArray();
        }

        //! number of elements in dimension 1
        size_t getLength1() const { return (last1_ + 1); }
        //! number of elements in dimension 2 at lower dimension index x
        //! \param[in]    x       index in dimension 1 for which the array
length in dimension 2 is requested
        size_t getLength2() const { return (last2_ + 1); }
        //! number of elements in dimension 3 at lower dimension indices x, y
        //! \param[in]    x       index in dimension 1
        //! \param[in]    y       index in dimension 2
        size_t getLength3() const { return (last3_ + 1); }

        //! get the last index of dimension 1
        size_t getLast1() const { return last1_; }
        //! get the last index of dimension 2 at lower dimension index x
        //! \param[in]    x       index in dimension 1 for which the last index
in dimension 2 is requested
        size_t getLast2(const size_t x) const
        {
            // Here, scan-build gives a false positive warning for a nullptr
dereference
            // which can not happen, because isIrreg_ is constant false, but
scan-build
            // assumes the condition is true (see HTML output).
            return (isIrreg_ ? irreg_[x] : last2_);
        }

        //! get the last index of dimension 3 for lower dimension indices x, y
        //! \param[in]    x       index in dimension 1
        //! \param[in]    y       index in dimension 2
        size_t getLast3(const size_t x, const size_t y) const
        {
            return last3_;
        }

        void deallocateArray()
        {
            if (arr_ != nullptr)
            {
// Removing this output also lets the warning vanish in the reduced example,
but not in the original code.
// Other modifications, like exchanging the getLastN() calls in the loops by
lastN_ also silences the warning.
std::cout << "irreg_ = " << (irreg_ == nullptr ? "nullptr" : "non-nullpr") <<
std::endl;
                // destroy the array elements
                for (size_t i = 0; i <= getLast1(); ++i)
                {
                    for (size_t j = 0; j <= getLast2(i); ++j)
                    {
                        for (size_t k = 0; k <= getLast3(i, j); ++k)
                        {
                           
std::allocator_traits<allocator_type>::destroy(allocator_, &this->operator()(i,
j, k));
                        }
                    }
                }

                for (size_t i = 0; i <= getLast1(); i++)
                {
                    for (size_t j = 0; j <= getLast2(i); ++j)
                    {
                       
std::allocator_traits<allocator_type>::deallocate(allocator_, arr_[i][j],
getLength3());
                    }
                   
std::allocator_traits<p_allocator_type>::deallocate(p_allocator_, arr_[i],
getLength2());
                }
               
std::allocator_traits<p2_allocator_type>::deallocate(p2_allocator_, arr_,
getLength1());

                arr_ = nullptr;
            }

            last1_  = -1;
            last2_  = -1;
            last3_  = -1;
        }

        //! destructor
        ~IrregArray3D()
        {
            deallocateArray();
        }

        // index operators
        /*! \brief   index operator(x,y,z) for functor-like usage
            \param[in]   x   index in dimension 1
            \param[in]   y   index in dimension 2
            \param[in]   z   index in dimension 3
         */
        value_type &operator()(size_t x, size_t y, size_t z)
        {
            return arr_[x][y][z];
        }

        //! returns true if the array is irregular
        inline bool isIrreg() const { return isIrreg_; }

    private:

        //! \brief allocate memory for the array using the preset array
geometry
        void allocateArray()
        {
            try
            {
                arr_ =
std::allocator_traits<p2_allocator_type>::allocate(p2_allocator_,
getLength1());
                for (size_t i = 0; i <= getLast1(); ++i)
                {
                    arr_[i]  =
std::allocator_traits<p_allocator_type>::allocate(p_allocator_, getLength2());
                }
                for (size_t i = 0; i <= getLast1(); i++)
                {
                    for (size_t j = 0; j <= getLast2(i); ++j)
                    {
                        arr_[i][j]  =
std::allocator_traits<allocator_type>::allocate(allocator_, getLength3());
                    }
                }
            }
            catch (const std::bad_alloc &)
            {
                std::fprintf(stderr, "Error in %s::allocateArray(): could not
allocate memory for the data array.", typeid(*this).name());
                throw;
            }

            // initialize the array elements
            for (size_t i = 0; i <= getLast1(); ++i)
            {
                for (size_t j = 0; j <= getLast2(i); ++j)
                {
                    for (size_t k = 0; k <= getLast3(i, j); ++k)
                    {
                       
std::allocator_traits<allocator_type>::construct(allocator_,
&this->operator()(i, j, k), value_type());
                    }
                }
            }
        }
};


int main ()
{
    // construct a 2x2x2 3D array, choosing array dimension 1x1x1 lets the
warning vanish
    // as many other small modifications.
    IrregArray3D<float> test_arr(1, 1, 1);

    // When destructing test_arr, scan-build warns for a nullptr dereference
    // which can not happen, and does not happen as verified with valgrind.

    return 0;
}</pre>
        </div>
      </p>


      <hr>
      <span>You are receiving this mail because:</span>

      <ul>
          <li>You are on the CC list for the bug.</li>
      </ul>
    </body>
</html>