[llvm-bugs] [Bug 47054] New: core.NullDereference despite field being assumed non-null with union-based type punning

via llvm-bugs llvm-bugs at lists.llvm.org
Sat Aug 8 05:12:27 PDT 2020


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

            Bug ID: 47054
           Summary: core.NullDereference despite field being assumed
                    non-null with union-based type punning
           Product: clang
           Version: trunk
          Hardware: All
                OS: All
            Status: NEW
          Severity: normal
          Priority: P
         Component: Static Analyzer
          Assignee: dcoughlin at apple.com
          Reporter: aaronpuchert at alice-dsl.net
                CC: dcoughlin at apple.com, llvm-bugs at lists.llvm.org

Created attachment 23831
  --> https://bugs.llvm.org/attachment.cgi?id=23831&action=edit
Reproducer, compile with clang --analyze.

The attached code produces what I believe is a false positive. It starts here:

struct CommonData {
    uint64_t* compilationMemSize = nullptr;
    Heap* temp_heap = nullptr;
};

class Postprocessor {
public:
    void run();

private:
    shared_ptr<CommonData> m_commonData;
};

void Postprocessor::run()
{
    auto data = m_commonData;
    if (data->temp_heap)
        *data->compilationMemSize = data->temp_heap->get_used_size();
}

then it goes into

uint64_t get_used_size() const
{
    const frame_type* p = &first_frame;
    uint64_t total_size = 0;
    while (p && p!=last_frame)
    {
        total_size += p->size;
        p = p->next;
    }

    return total_size + last_frame_usage;
}

where the check fires on the last line:

analyzer-bug.cpp:100:9: note: Assuming field 'temp_heap' is non-null
    if (data->temp_heap)
        ^~~~~~~~~~~~~~~
analyzer-bug.cpp:100:5: note: Taking true branch
    if (data->temp_heap)
    ^
analyzer-bug.cpp:101:37: note: Calling 'Heap::get_used_size'
        *data->compilationMemSize = data->temp_heap->get_used_size();
                                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
analyzer-bug.cpp:10:16: note: Assuming 'p' is null
        while (p && p!=last_frame)
               ^
analyzer-bug.cpp:10:18: note: Left side of '&&' is false
        while (p && p!=last_frame)
                 ^
analyzer-bug.cpp:16:29: note: Access to field 'last_frame_usage' results in a
dereference of a null pointer
        return total_size + last_frame_usage;
                            ^~~~~~~~~~~~~~~~

Note that temp_heap is assumed as non-null, and p cannot be null in the first
iteration. The interesting part is the shared_ptr implementation, which uses
union-based type punning. Replacing this by reinterpret_cast or
__builtin_memcpy makes the issue disappear.

template<typename T> class mem_conv
{
    template<typename U> union UCast {
        U from_;
        T to_;

        constexpr explicit UCast(U x) noexcept : from_(x) {}
    };

public:
    template<typename TM_FROM> constexpr
    static T cast(TM_FROM from) noexcept
    {
        static_assert(sizeof(TM_FROM) == sizeof(T));

        UCast<TM_FROM> tmp(from);
        return tmp.to_;
    }
};

Now technically union-based type punning is not allowed according to the
standard, but I believe that Clang supports it.

The reproducer is still a bit large, but I wasn't able to reduce it further, it
seems that multiple things need to come together here.

-- 
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/20200808/0227e97d/attachment-0001.html>


More information about the llvm-bugs mailing list