Avoid undefined-behavior

If a struct had (because it's a union) sizeof that is larger than the null_size,
we were providing only null_size bytes for its Null object. We know we'd never
access beyond that, but is undefined-behavior nonetheless according to the
standard.

The alternative fix would have required use of flexible-arrays, which are not
standard and have their own issues in various compiler. We've discussed that
extensively in the follow Mozilla issue (currently locked; I've asked that it
be opened):

  https://bugzilla.mozilla.org/show_bug.cgi?id=1577584

Part of
https://github.com/harfbuzz/harfbuzz/pull/2067
This commit is contained in:
Behdad Esfahbod 2021-04-16 13:22:05 -06:00
parent 499248c533
commit 23a28f5ad0
1 changed files with 8 additions and 3 deletions

View File

@ -39,8 +39,11 @@
#define HB_NULL_POOL_SIZE 384 #define HB_NULL_POOL_SIZE 384
/* Use SFINAE to sniff whether T has min_size; in which case return T::null_size, /* Use SFINAE to sniff whether T has min_size; in which case return the larger
* otherwise return sizeof(T). */ * of sizeof(T) and T::null_size, otherwise return sizeof(T).
*
* The main purpose of this is to let structs communicate that they are not nullable,
* by defining min_size but *not* null_size. */
/* The hard way... /* The hard way...
* https://stackoverflow.com/questions/7776448/sfinae-tried-with-bool-gives-compiler-error-template-argument-tvalue-invol * https://stackoverflow.com/questions/7776448/sfinae-tried-with-bool-gives-compiler-error-template-argument-tvalue-invol
@ -49,7 +52,9 @@
template <typename T, typename> template <typename T, typename>
struct _hb_null_size : hb_integral_constant<unsigned, sizeof (T)> {}; struct _hb_null_size : hb_integral_constant<unsigned, sizeof (T)> {};
template <typename T> template <typename T>
struct _hb_null_size<T, hb_void_t<decltype (T::min_size)>> : hb_integral_constant<unsigned, T::null_size> {}; struct _hb_null_size<T, hb_void_t<decltype (T::min_size)>>
: hb_integral_constant<unsigned,
(sizeof (T) > T::null_size ? sizeof (T) : T::null_size)> {};
template <typename T> template <typename T>
using hb_null_size = _hb_null_size<T, void>; using hb_null_size = _hb_null_size<T, void>;
#define hb_null_size(T) hb_null_size<T>::value #define hb_null_size(T) hb_null_size<T>::value