provide a hint to clang-analyzer-optin.portability.UnixAPI?

To whom it may concern,

tl;dr I think I found a bug in the clang static analyzer. Could someone please help me find a workaround/where to properly report the bug?

I have a statement in a class initializer list:

data_ptr((rhs.has_data() && rhs.size_in_bytes() > 0)? malloc(rhs.size_in_bytes()) : nullptr)

rhs.has_data() is a const function that returns t/f if rhs.data_ptr != null

rhs.size_in_bytes() a const function that returns the number of bytes in the structure if has_data was true

i.e. it is possible that rhs.has_data() is false, and rhs.size_in_bytes() > 0

However the clang static analyzer seems to get a false positive here.

/usr/lib/gcc/x86_64-pc-linux-gnu/8.3.0/include/g++-v8/bits/unique_ptr.h:831:34: note: Calling copy constructor for 'pressio_data'
../include/libpressio_ext/cpp/data.h:247:15: note: Left side of '&&' is true
    data_ptr((rhs.has_data() && rhs.size_in_bytes() > 0)? malloc(rhs.size_in_bytes()) : nullptr),
              ^
../include/libpressio_ext/cpp/data.h:247:33: note: Assuming the condition is true
    data_ptr((rhs.has_data() && rhs.size_in_bytes() > 0)? malloc(rhs.size_in_bytes()) : nullptr),
                                ^
../include/libpressio_ext/cpp/data.h:247:14: note: '?' condition is true
    data_ptr((rhs.has_data() && rhs.size_in_bytes() > 0)? malloc(rhs.size_in_bytes()) : nullptr),
             ^
../include/libpressio_ext/cpp/data.h:247:66: note: Calling 'pressio_data::size_in_bytes'
    data_ptr((rhs.has_data() && rhs.size_in_bytes() > 0)? malloc(rhs.size_in_bytes()) : nullptr),
                                                                 ^
../include/libpressio_ext/cpp/data.h:384:12: note: Calling 'data_size_in_bytes<unsigned long>'
    return data_size_in_bytes(data_dtype, num_dimensions(), dims.data());
           ^
../include/libpressio_ext/cpp/data.h:31:5: note: Returning zero
    return data_size_in_elements(dimensions, dims) * pressio_dtype_size(type);
    ^
../include/libpressio_ext/cpp/data.h:384:12: note: Returning from 'data_size_in_bytes<unsigned long>'
    return data_size_in_bytes(data_dtype, num_dimensions(), dims.data());
           ^
../include/libpressio_ext/cpp/data.h:384:5: note: Returning zero
    return data_size_in_bytes(data_dtype, num_dimensions(), dims.data());
    ^
../include/libpressio_ext/cpp/data.h:247:66: note: Returning from 'pressio_data::size_in_bytes'
    data_ptr((rhs.has_data() && rhs.size_in_bytes() > 0)? malloc(rhs.size_in_bytes()) : nullptr),
                                                                 ^
../include/libpressio_ext/cpp/data.h:247:59: note: Call to 'malloc' has an allocation size of 0 bytes
    data_ptr((rhs.has_data() && rhs.size_in_bytes() > 0)? malloc(rhs.size_in_bytes()) : nullptr),

Is there a way to instruct the static analyzer that malloc cannot be called with size_in_bytes == 0 because saying (rhs.size_in_bytes() > 0) isn’t enough. I’m using clang-9.0.1 on gentoo.

Respectfully,
Robert Underwood

It looks like we don't understand that size_in_bytes() always returns the same value. There may be multiple reasons for this. I'd like to see the whole code in order to understand what's going on. Could you produce a preprocessed file and attach it together with the analyzer invocation?

As a workaround / suppression you should be able to do something like this:

static void *make_data_ptr(RHS rhs) {
if (!rhs.has_data())
return nullptr;

 size\_t size = rhs\.size\_in\_bytes\(\);
 return size ? malloc\(size\) : nullptr;

}

Ctor(RHS rhs): data_ptr(make_data_ptr(rhs)) {}

This would prevent the analyzer (and, well, your actual generated code) from calling size_in_bytes() twice and believing that it may return two different values.

I might be wrong, but probably this is the line which triggered the warning.

https://github.com/robertu94/libpressio/blob/master/include/libpressio_ext/cpp/data.h#L247

Yeah but it doesn't help much, i'm more interested in intermediate events across the path.

All,

Sorry for the delay, was on break then adjusting to a new workspace as I expect many of us are.

I have attached the prepossessed file you asked for.

Balázs was correct about the code triggering the bug. It is from a project that I wrote/maintain called libpressio.

You can also reproduce build with the following script. The default build features the bug, and it doesn’t have any dependencies outside a relatively recent standard library (c++17)

First, remove the reference to -clang-analyzer-optin.portability.UnixAPI in the .clang-tidy file.

git clone [https://github.com/robertu94/libpressio](https://github.com/robertu94/libpressio)
mkdir build
cd build
cmake .. -DUSE_CLANG_TIDY=ON
cd ..
cmake --build ./build

You can then run clang-tidy for an offending file with:

clang-tidy -p build ./src/pressio_options.cc

pressio_options.cc.tgz (213 KB)