Changeset 205066 in webkit


Ignore:
Timestamp:
Aug 26, 2016 7:39:39 PM (8 years ago)
Author:
commit-queue@webkit.org
Message:

bitwise_cast uses inactive member of union
https://bugs.webkit.org/show_bug.cgi?id=161244

Patch by JF Bastien <jfbastien@apple.com> on 2016-08-26
Reviewed by Benjamin Poulain.

  • wtf/Compiler.h:

Add COMPILER_HAS_CLANG_FEATURE

  • wtf/StdLibExtras.h:

(WTF::bitwise_cast):
Fix C++ UB, add trivially-copyable check.

bitwise_cast stores into a union with one type and reads with
another, which is technically C++ undefined behavior because it's
accessing the wrong active member of the union. The better way to
do this is through memcpy, which compilers optimize as well
because it's known-size in known-not-to-escape storage (for small
types they'll inline and then convert stack memory access to SSA
values which may be in-register if that makes sense, which would
be a move between int/FP registers at worst).

The C++ Standard's section [basic.types] explicitly blesses memcpy:

For any trivially copyable type T, if two pointers to T point to
distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a
base-class subobject, if the underlying bytes (1.7) making up obj1
are copied into obj2, 42 obj2 shall subsequently hold the same
value as obj1.

[Example:

T* t1p;
T* t2p;
provided that t2p points to an initialized object ...
std::memcpy(t1p, t2p, sizeof(T));
at this point, every subobject of trivially copyable type in *t1p contains
the same value as the corresponding subobject in *t2p

— end example ]

Whereas section [class.union] says:

In a union, at most one of the non-static data members can be
active at any time, that is, the value of at most one of the
non-static data members can be stored in a union at any time.

While we're at it, checking that sizeof(To) == sizeof(From) is
good, but we should also check that both types are trivially
copyable (can have a ctor, no dtor, and copy is defaulted as if by
memcpy for type and all subtypes). Unfortunately that trait isn't
implemented consistently in all recent compiler+stdlib
implementations, but recent clang has an equivalent builtin
(other compilers simply won't do the check, and will break on bots
with the right compilers which is better than the current silent
breakage). This builtin hack also avoids #include <type_traits>
which really doesn't save much.

Location:
trunk/Source/WTF
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WTF/ChangeLog

    r205062 r205066  
     12016-08-26  JF Bastien  <jfbastien@apple.com>
     2
     3        bitwise_cast uses inactive member of union
     4        https://bugs.webkit.org/show_bug.cgi?id=161244
     5
     6        Reviewed by Benjamin Poulain.
     7
     8        * wtf/Compiler.h:
     9        Add COMPILER_HAS_CLANG_FEATURE
     10        * wtf/StdLibExtras.h:
     11        (WTF::bitwise_cast):
     12        Fix C++ UB, add trivially-copyable check.
     13
     14        bitwise_cast stores into a union with one type and reads with
     15        another, which is technically C++ undefined behavior because it's
     16        accessing the wrong active member of the union. The better way to
     17        do this is through memcpy, which compilers optimize as well
     18        because it's known-size in known-not-to-escape storage (for small
     19        types they'll inline and then convert stack memory access to SSA
     20        values which may be in-register if that makes sense, which would
     21        be a move between int/FP registers at worst).
     22
     23        The C++ Standard's section [basic.types] explicitly blesses memcpy:
     24
     25          For any trivially copyable type T, if two pointers to T point to
     26          distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a
     27          base-class subobject, if the underlying bytes (1.7) making up obj1
     28          are copied into obj2, 42 obj2 shall subsequently hold the same
     29          value as obj1.
     30
     31          [Example:
     32            T* t1p;
     33            T* t2p;
     34            // provided that t2p points to an initialized object ...
     35            std::memcpy(t1p, t2p, sizeof(T));
     36            // at this point, every subobject of trivially copyable type in *t1p contains
     37            // the same value as the corresponding subobject in *t2p
     38          — end example ]
     39
     40        Whereas section [class.union] says:
     41
     42          In a union, at most one of the non-static data members can be
     43          active at any time, that is, the value of at most one of the
     44          non-static data members can be stored in a union at any time.
     45
     46        While we're at it, checking that sizeof(To) == sizeof(From) is
     47        good, but we should also check that both types are trivially
     48        copyable (can have a ctor, no dtor, and copy is defaulted as if by
     49        memcpy for type and all subtypes). Unfortunately that trait isn't
     50        implemented consistently in all recent compiler+stdlib
     51        implementations, but recent clang has an equivalent builtin
     52        (other compilers simply won't do the check, and will break on bots
     53        with the right compilers which is better than the current silent
     54        breakage). This builtin hack also avoids #include <type_traits>
     55        which really doesn't save much.
     56
    1572016-08-26  Johan K. Jensen  <johan_jensen@apple.com>
    258
  • trunk/Source/WTF/wtf/Compiler.h

    r204433 r205066  
    3636#define COMPILER_QUIRK(WTF_COMPILER_QUIRK) (defined WTF_COMPILER_QUIRK_##WTF_COMPILER_QUIRK  && WTF_COMPILER_QUIRK_##WTF_COMPILER_QUIRK)
    3737
    38 /* COMPILER_HAS_CLANG_BUILTIN() - wether the compiler supports a particular clang builtin. */
     38/* COMPILER_HAS_CLANG_BUILTIN() - whether the compiler supports a particular clang builtin. */
    3939#ifdef __has_builtin
    4040#define COMPILER_HAS_CLANG_BUILTIN(x) __has_builtin(x)
     
    4343#endif
    4444
     45/* COMPILER_HAS_CLANG_HEATURE() - whether the compiler supports a particular language or library feature. */
     46/* http://clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension */
     47#ifdef __has_feature
     48#define COMPILER_HAS_CLANG_FEATURE(x) __has_feature(x)
     49#else
     50#define COMPILER_HAS_CLANG_FEATURE(x) 0
     51#endif
     52
    4553/* ==== COMPILER() - primary detection of the compiler being used to build the project, in alphabetical order ==== */
    4654
     
    4957#if defined(__clang__)
    5058#define WTF_COMPILER_CLANG 1
    51 #define WTF_COMPILER_SUPPORTS_BLOCKS __has_feature(blocks)
    52 #define WTF_COMPILER_SUPPORTS_C_STATIC_ASSERT __has_feature(c_static_assert)
    53 #define WTF_COMPILER_SUPPORTS_CXX_REFERENCE_QUALIFIED_FUNCTIONS __has_feature(cxx_reference_qualified_functions)
    54 #define WTF_COMPILER_SUPPORTS_CXX_USER_LITERALS __has_feature(cxx_user_literals)
    55 #define WTF_COMPILER_SUPPORTS_FALLTHROUGH_WARNINGS __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough")
    56 #define WTF_COMPILER_SUPPORTS_CXX_EXCEPTIONS __has_feature(cxx_exceptions)
     59#define WTF_COMPILER_SUPPORTS_BLOCKS COMPILER_HAS_CLANG_FEATURE(blocks)
     60#define WTF_COMPILER_SUPPORTS_C_STATIC_ASSERT COMPILER_HAS_CLANG_FEATURE(c_static_assert)
     61#define WTF_COMPILER_SUPPORTS_CXX_REFERENCE_QUALIFIED_FUNCTIONS COMPILER_HAS_CLANG_FEATURE(cxx_reference_qualified_functions)
     62#define WTF_COMPILER_SUPPORTS_CXX_USER_LITERALS COMPILER_HAS_CLANG_FEATURE(cxx_user_literals)
     63#define WTF_COMPILER_SUPPORTS_FALLTHROUGH_WARNINGS COMPILER_HAS_CLANG_FEATURE(cxx_attributes) && __has_warning("-Wimplicit-fallthrough")
     64#define WTF_COMPILER_SUPPORTS_CXX_EXCEPTIONS COMPILER_HAS_CLANG_FEATURE(cxx_exceptions)
     65#define WTF_COMPILER_SUPPORTS_BUILTIN_IS_TRIVIALLY_COPYABLE COMPILER_HAS_CLANG_FEATURE(is_trivially_copyable)
    5766
    5867#ifdef __cplusplus
     
    136145#endif
    137146
    138 #if defined(__has_feature)
    139 #define ASAN_ENABLED __has_feature(address_sanitizer)
    140 #else
    141 #define ASAN_ENABLED 0
    142 #endif
     147#define ASAN_ENABLED COMPILER_HAS_CLANG_FEATURE(address_sanitizer)
    143148
    144149#if ASAN_ENABLED
  • trunk/Source/WTF/wtf/StdLibExtras.h

    r204992 r205066  
    2929
    3030#include <chrono>
     31#include <cstring>
    3132#include <memory>
    32 #include <string.h>
    3333#include <wtf/Assertions.h>
    3434#include <wtf/CheckedArithmetic.h>
     35#include <wtf/Compiler.h>
    3536
    3637// This was used to declare and define a static local variable (static T;) so that
     
    146147{
    147148    static_assert(sizeof(FromType) == sizeof(ToType), "bitwise_cast size of FromType and ToType must be equal!");
    148     union {
    149         FromType from;
    150         ToType to;
    151     } u;
    152     u.from = from;
    153     return u.to;
     149#if COMPILER_SUPPORTS(BUILTIN_IS_TRIVIALLY_COPYABLE)
     150    // Not all recent STL implementations support the std::is_trivially_copyable type trait. Work around this by only checking on toolchains which have the equivalent compiler intrinsic.
     151    static_assert(__is_trivially_copyable(ToType), "bitwise_cast of non-trivially-copyable type!");
     152    static_assert(__is_trivially_copyable(FromType), "bitwise_cast of non-trivially-copyable type!");
     153#endif
     154    ToType to;
     155    std::memcpy(&to, &from, sizeof(to));
     156    return to;
    154157}
    155158
Note: See TracChangeset for help on using the changeset viewer.