[cfe-dev] [libc++] std::less and cxa_demangle.cpp

Matthew Dempsky matthew at dempsky.org
Mon Jun 17 14:45:10 PDT 2013


[+cfe-dev, since I think this might interest some Clang developers/C++
standards sticklers]

On Mon, Jun 17, 2013 at 04:29:36PM -0400, Howard Hinnant wrote:
> On Jun 17, 2013, at 4:02 PM, Matthew Dempsky <matthew at dempsky.org> wrote:
> > 5. The C++ standard only defines relational operators for pointers to
> > elements of the same array, so arena::pointer_in_buffer() triggers
> > undefined behavior whenever it's used to test a pointer allocated by
> > malloc().  The best solution to this that I know of is to do the
> > comparisons on integers instead, and hope that the compiler does
> > implement an "unsurprising" pointer-to-integer mapping as encouraged
> > (but not required) by the standard.
> 
> It isn't undefined behavior if I use std::less ([comparisons]/p14),
> and I'm taking advantage of the fact that on all platforms being
> targeted, std::less<T*> is implemented with nothing more complicated
> than pointer comparison.

Hm, true, the standard does guarantee std::less imposes a total
ordering on pointers, though I don't believe that forbids orderings
that interleave pointers to distinct arrays.  E.g., it seems perfectly
standards conforming (but "surprising") for this program to output 1:

    #include <functional>
    #include <iostream>
    int main() {
        std::less<char *> lt;
        char x, y;
        std::cout << (lt(&x, &y) && lt(&y, &x + 1)) << std::endl;
    }

So I think this actually reveals a bug in libc++'s implementation of
std::less, etc.  They need partial specializations for pointer types
to properly guarantee a total ordering, otherwise the above code
triggers undefined behavior with libc++'s current definitions due to
the pointer comparisons between &y and &x (and between &y and &x + 1).


We should then also utilize std::less_equal in cxa_demangle.cpp
instead of directly comparing pointers.  If we wanted to go further,
in arena::allocate() we could assert that the pointer returned by
std::malloc() does not satisfy pointer_in_buffer().


Index: libcxx/include/__functional_base
===================================================================
--- libcxx/include/__functional_base	(revision 184100)
+++ libcxx/include/__functional_base	(working copy)
@@ -12,6 +12,7 @@
 #define _LIBCPP_FUNCTIONAL_BASE
 
 #include <__config>
+#include <cstdint>  // for uintptr_t
 #include <type_traits>
 #include <typeinfo>
 #include <exception>
@@ -57,6 +58,13 @@
         {return __x < __y;}
 };
 
+template <class _Tp>
+struct _LIBCPP_TYPE_VIS less<_Tp*> : binary_function<_Tp*, _Tp*, bool>
+{
+    _LIBCPP_INLINE_VISIBILITY bool operator()(_Tp* const& __x, _Tp* const& __y) const
+        {return reinterpret_cast<std::uintptr_t>(__x) < reinterpret_cast<std::uintptr_t>(__y);}
+};
+
 #ifdef _LIBCPP_HAS_NO_VARIADICS
 
 #include <__functional_base_03>
Index: libcxx/include/functional
===================================================================
--- libcxx/include/functional	(revision 184100)
+++ libcxx/include/functional	(working copy)
@@ -536,6 +536,13 @@
         {return __x > __y;}
 };
 
+template <class _Tp>
+struct _LIBCPP_TYPE_VIS greater<_Tp*> : binary_function<_Tp*, _Tp*, bool>
+{
+    _LIBCPP_INLINE_VISIBILITY bool operator()(_Tp* const& __x, _Tp* const& __y) const
+        {return reinterpret_cast<std::uintptr_t>(__x) > reinterpret_cast<std::uintptr_t>(__y);}
+};
+
 // less in <__functional_base>
 
 template <class _Tp>
@@ -546,6 +553,13 @@
 };
 
 template <class _Tp>
+struct _LIBCPP_TYPE_VIS greater_equal<_Tp*> : binary_function<_Tp*, _Tp*, bool>
+{
+    _LIBCPP_INLINE_VISIBILITY bool operator()(_Tp* const& __x, _Tp* const& __y) const
+        {return reinterpret_cast<std::uintptr_t>(__x) >= reinterpret_cast<std::uintptr_t>(__y);}
+};
+
+template <class _Tp>
 struct _LIBCPP_TYPE_VIS less_equal : binary_function<_Tp, _Tp, bool>
 {
     _LIBCPP_INLINE_VISIBILITY bool operator()(const _Tp& __x, const _Tp& __y) const
@@ -553,6 +567,13 @@
 };
 
 template <class _Tp>
+struct _LIBCPP_TYPE_VIS less_equal<_Tp*> : binary_function<_Tp*, _Tp*, bool>
+{
+    _LIBCPP_INLINE_VISIBILITY bool operator()(_Tp* const & __x, _Tp* const & __y) const
+        {return reinterpret_cast<std::uintptr_t>(__x) <= reinterpret_cast<std::uintptr_t>(__y);}
+};
+
+template <class _Tp>
 struct _LIBCPP_TYPE_VIS logical_and : binary_function<_Tp, _Tp, bool>
 {
     _LIBCPP_INLINE_VISIBILITY bool operator()(const _Tp& __x, const _Tp& __y) const
Index: libcxxabi/src/cxa_demangle.cpp
===================================================================
--- libcxxabi/src/cxa_demangle.cpp	(revision 184120)
+++ libcxxabi/src/cxa_demangle.cpp	(working copy)
@@ -14,6 +14,7 @@
 #include <algorithm>
 #include <string>
 #include <numeric>
+#include <functional>
 #include <cstdlib>
 #include <cstring>
 #include <cctype>
@@ -4222,7 +4223,11 @@
 
     bool
     pointer_in_buffer(char* p) noexcept
-        {return buf_ <= p && p <= buf_ + N;}
+    {
+        // We assume an "unsurprising" total order on pointers here.
+        std::less_equal<const char*> lteq;
+        return lteq(buf_, p) && lteq(p, buf_ + N);
+    }
 
 public:
     arena() noexcept : ptr_(buf_) {}



More information about the cfe-dev mailing list