<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>