[PATCH] D33588: Fix two sources of UB in __next_hash_pow2 (from __hash_table)

Vedant Kumar via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Fri May 26 02:14:53 PDT 2017

vsk created this revision.

There are two sources of undefined behavior in __next_hash_pow2: an
invalid shift (occurs when __n is 0 or 1), and an invalid call to CLZ
(when __n=1).

This patch corrects both issues. It's NFC for all values of __n which do
not trigger UB, and leads to defined results when __n is 0 or 1.

Minimal reproducer:

  unordered_map<uint64_t, unsigned long> m;

I wrote a miniature 'fuzzer' to test this change and ran it with UBSan
enabled. AFAIK, this is the best we can do for a test. I don't know how
to write a non-flaky test which would fail without this patch applied.
Here is the 'fuzzer' (simply run with -fsanitize=undefined):

  void fuzzUnorderedMap(unsigned NumInserts, unsigned NumReserve1,
                        unsigned NumReserve2) {
    unordered_map<uint64_t, unsigned long> m;
    for (unsigned I = 0; I < NumInserts; ++I) { m[I] = 0; }
    size_t __n = size_t(ceil(float(m.size()) / m.max_load_factor()));
  for (unsigned NumInserts = 0; NumInserts <= 64; ++NumInserts)
    for (unsigned NumReserve1 = 1; NumReserve1 <= 64; ++NumReserve1)
      for (unsigned NumReserve2 = 1; NumReserve2 <= 64; ++NumReserve2)
        fuzzUnorderedMap(NumInserts, NumReserve1, NumReserve2);




Index: include/__hash_table
--- include/__hash_table
+++ include/__hash_table
@@ -136,7 +136,7 @@
 __next_hash_pow2(size_t __n)
-    return size_t(1) << (std::numeric_limits<size_t>::digits - __clz(__n-1));
+    return (__n > 1) ? (size_t(1) << (std::numeric_limits<size_t>::digits - __clz(__n-1))) : __n;

-------------- next part --------------
A non-text attachment was scrubbed...
Name: D33588.100375.patch
Type: text/x-patch
Size: 392 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20170526/38b6d1bb/attachment.bin>

More information about the cfe-commits mailing list