From 4cdd915fdad4885f8dd4503bc07a6ab991bee2db Mon Sep 17 00:00:00 2001 From: Eric Fiselier Date: Wed, 8 Feb 2017 00:10:10 +0000 Subject: [PATCH] Prevent UBSAN from generating unsigned overflow diagnostics in the hashing internals llvm-svn: 294391 --- libcxx/include/utility | 33 ++++++++++----- ...h_ubsan_unsigned_overflow_ignored.pass.cpp | 41 +++++++++++++++++++ libcxx/test/support/test_macros.h | 12 ++++++ 3 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 libcxx/test/libcxx/utilities/function.objects/unord.hash/murmur2_or_cityhash_ubsan_unsigned_overflow_ignored.pass.cpp diff --git a/libcxx/include/utility b/libcxx/include/utility index c230511b37d1..3452fe119ac7 100644 --- a/libcxx/include/utility +++ b/libcxx/include/utility @@ -959,13 +959,14 @@ struct __murmur2_or_cityhash; template struct __murmur2_or_cityhash<_Size, 32> { - _Size operator()(const void* __key, _Size __len); + inline _Size operator()(const void* __key, _Size __len) + _LIBCPP_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK; }; // murmur2 template _Size -__murmur2_or_cityhash<_Size, 32>::operator()(const void* __key, _Size __len) _LIBCPP_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK +__murmur2_or_cityhash<_Size, 32>::operator()(const void* __key, _Size __len) { const _Size __m = 0x5bd1e995; const _Size __r = 24; @@ -999,7 +1000,7 @@ __murmur2_or_cityhash<_Size, 32>::operator()(const void* __key, _Size __len) _LI template struct __murmur2_or_cityhash<_Size, 64> { - _Size operator()(const void* __key, _Size __len); + inline _Size operator()(const void* __key, _Size __len) _LIBCPP_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK; private: // Some primes between 2^63 and 2^64. @@ -1020,7 +1021,9 @@ struct __murmur2_or_cityhash<_Size, 64> return __val ^ (__val >> 47); } - static _Size __hash_len_16(_Size __u, _Size __v) { + static _Size __hash_len_16(_Size __u, _Size __v) + _LIBCPP_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK + { const _Size __mul = 0x9ddfea08eb382d69ULL; _Size __a = (__u ^ __v) * __mul; __a ^= (__a >> 47); @@ -1030,7 +1033,9 @@ struct __murmur2_or_cityhash<_Size, 64> return __b; } - static _Size __hash_len_0_to_16(const char* __s, _Size __len) { + static _Size __hash_len_0_to_16(const char* __s, _Size __len) + _LIBCPP_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK + { if (__len > 8) { const _Size __a = __loadword<_Size>(__s); const _Size __b = __loadword<_Size>(__s + __len - 8); @@ -1053,7 +1058,9 @@ struct __murmur2_or_cityhash<_Size, 64> return __k2; } - static _Size __hash_len_17_to_32(const char *__s, _Size __len) { + static _Size __hash_len_17_to_32(const char *__s, _Size __len) + _LIBCPP_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK + { const _Size __a = __loadword<_Size>(__s) * __k1; const _Size __b = __loadword<_Size>(__s + 8); const _Size __c = __loadword<_Size>(__s + __len - 8) * __k2; @@ -1065,7 +1072,9 @@ struct __murmur2_or_cityhash<_Size, 64> // Return a 16-byte hash for 48 bytes. Quick and dirty. // Callers do best to use "random-looking" values for a and b. static pair<_Size, _Size> __weak_hash_len_32_with_seeds( - _Size __w, _Size __x, _Size __y, _Size __z, _Size __a, _Size __b) { + _Size __w, _Size __x, _Size __y, _Size __z, _Size __a, _Size __b) + _LIBCPP_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK + { __a += __w; __b = __rotate(__b + __a + __z, 21); const _Size __c = __a; @@ -1077,7 +1086,9 @@ struct __murmur2_or_cityhash<_Size, 64> // Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty. static pair<_Size, _Size> __weak_hash_len_32_with_seeds( - const char* __s, _Size __a, _Size __b) { + const char* __s, _Size __a, _Size __b) + _LIBCPP_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK + { return __weak_hash_len_32_with_seeds(__loadword<_Size>(__s), __loadword<_Size>(__s + 8), __loadword<_Size>(__s + 16), @@ -1087,7 +1098,9 @@ struct __murmur2_or_cityhash<_Size, 64> } // Return an 8-byte hash for 33 to 64 bytes. - static _Size __hash_len_33_to_64(const char *__s, size_t __len) { + static _Size __hash_len_33_to_64(const char *__s, size_t __len) + _LIBCPP_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK + { _Size __z = __loadword<_Size>(__s + 24); _Size __a = __loadword<_Size>(__s) + (__len + __loadword<_Size>(__s + __len - 16)) * __k0; @@ -1115,7 +1128,7 @@ struct __murmur2_or_cityhash<_Size, 64> // cityhash64 template _Size -__murmur2_or_cityhash<_Size, 64>::operator()(const void* __key, _Size __len) _LIBCPP_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK +__murmur2_or_cityhash<_Size, 64>::operator()(const void* __key, _Size __len) { const char* __s = static_cast(__key); if (__len <= 32) { diff --git a/libcxx/test/libcxx/utilities/function.objects/unord.hash/murmur2_or_cityhash_ubsan_unsigned_overflow_ignored.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/unord.hash/murmur2_or_cityhash_ubsan_unsigned_overflow_ignored.pass.cpp new file mode 100644 index 000000000000..319a78b0506c --- /dev/null +++ b/libcxx/test/libcxx/utilities/function.objects/unord.hash/murmur2_or_cityhash_ubsan_unsigned_overflow_ignored.pass.cpp @@ -0,0 +1,41 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Test that UBSAN doesn't generate unsigned integer overflow diagnostics +// from within the hashing internals. + +#include +#include +#include +#include + +#include "test_macros.h" + +typedef std::__murmur2_or_cityhash Hash32; +typedef std::__murmur2_or_cityhash Hash64; + +void test(const void* key, int len) { + for (int i=1; i <= len; ++i) { + Hash32 h1; + Hash64 h2; + DoNotOptimize(h1(key, i)); + DoNotOptimize(h2(key, i)); + } +} + +int main() { + const std::string TestCases[] = { + "abcdaoeuaoeclaoeoaeuaoeuaousaotehu]+}sthoasuthaoesutahoesutaohesutaoeusaoetuhasoetuhaoseutaoseuthaoesutaohes" + "00000000000000000000000000000000000000000000000000000000000000000000000", + "1237546895+54+4554985416849484213464984765465464654564565645645646546456546546" + }; + const size_t NumCases = sizeof(TestCases)/sizeof(TestCases[0]); + for (size_t i=0; i < NumCases; ++i) + test(TestCases[i].data(), TestCases[i].length()); +} diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h index 431ca8a3e4b7..cec5f5a5e03f 100644 --- a/libcxx/test/support/test_macros.h +++ b/libcxx/test/support/test_macros.h @@ -182,6 +182,18 @@ struct is_same { enum {value = 1}; }; #endif #endif +#if defined(__GNUC__) || defined(__clang__) +template +inline void DoNotOptimize(Tp const& value) { + asm volatile("" : : "g"(value) : "memory"); +} +#else +template +inline void DoNotOptimize(Tp const&) { + // FIXME: Do something here... +} +#endif + #if defined(__GNUC__) #pragma GCC diagnostic pop #endif