[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