<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 - core.NullDereference despite field being assumed non-null with union-based type punning"
   href="https://bugs.llvm.org/show_bug.cgi?id=47054">47054</a>
          </td>
        </tr>

        <tr>
          <th>Summary</th>
          <td>core.NullDereference despite field being assumed non-null with union-based type punning
          </td>
        </tr>

        <tr>
          <th>Product</th>
          <td>clang
          </td>
        </tr>

        <tr>
          <th>Version</th>
          <td>trunk
          </td>
        </tr>

        <tr>
          <th>Hardware</th>
          <td>All
          </td>
        </tr>

        <tr>
          <th>OS</th>
          <td>All
          </td>
        </tr>

        <tr>
          <th>Status</th>
          <td>NEW
          </td>
        </tr>

        <tr>
          <th>Severity</th>
          <td>normal
          </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>aaronpuchert@alice-dsl.net
          </td>
        </tr>

        <tr>
          <th>CC</th>
          <td>dcoughlin@apple.com, llvm-bugs@lists.llvm.org
          </td>
        </tr></table>
      <p>
        <div>
        <pre>Created <span class=""><a href="attachment.cgi?id=23831" name="attach_23831" title="Reproducer, compile with clang --analyze.">attachment 23831</a> <a href="attachment.cgi?id=23831&action=edit" title="Reproducer, compile with clang --analyze.">[details]</a></span>
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.</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>