[llvm-bugs] [Bug 39064] New: scan-build reports a false positive nullptr dereference because it apparently incorrectly tracks the value of a class member

via llvm-bugs llvm-bugs at lists.llvm.org
Mon Sep 24 12:08:10 PDT 2018


https://bugs.llvm.org/show_bug.cgi?id=39064

            Bug ID: 39064
           Summary: scan-build reports a false positive nullptr
                    dereference because it apparently incorrectly tracks
                    the value of a class member
           Product: clang
           Version: trunk
          Hardware: PC
                OS: Linux
            Status: NEW
          Severity: enhancement
          Priority: P
         Component: Static Analyzer
          Assignee: dcoughlin at apple.com
          Reporter: thomas.ullmann at mpibpc.mpg.de
                CC: llvm-bugs at lists.llvm.org

Created attachment 20913
  --> https://bugs.llvm.org/attachment.cgi?id=20913&action=edit
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;
}

-- 
You are receiving this mail because:
You are on the CC list for the bug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-bugs/attachments/20180924/2516199b/attachment.html>


More information about the llvm-bugs mailing list