<random>: independent_bits_engine performs forbidden full shifts (original) (raw)

Found by Clang 20 UBSan in P0952R2_new_generate_canonical's usage of independent_bits_engine. Reduced:

D:\GitHub\STL\out\x64>type meow.cpp

#include #include #include #include #include using namespace std;

template <class Engine, size_t W> void test(const string_view name) { println("test<{}, {}>():", name, W); independent_bits_engine<Engine, W, uint64_t> ibe; const auto val = ibe(); println("val: {}", val); println(); }

int main() { test<mt19937_64, 64>("mt19937_64"); test<mt19937, 64>("mt19937"); test<mt19937, 32>("mt19937"); }

D:\GitHub\STL\out\x64>clang-cl /EHsc /nologo /W4 /std:c++latest -fsanitize=undefined meow.cpp /Zi /Fdmeow.pdb /link /ignore:4217 && meow
   Creating library meow.lib and object meow.exp
test<mt19937_64, 64>():
D:\GitHub\STL\out\x64\out\inc\random:1977:25: runtime error: shift exponent 64 is too large for 64-bit type '_Eres' (aka 'unsigned long long')
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior D:\GitHub\STL\out\x64\out\inc\random:1977:25
D:\GitHub\STL\out\x64\out\inc\random:1977:34: runtime error: shift exponent 64 is too large for 64-bit type '_Eres' (aka 'unsigned long long')
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior D:\GitHub\STL\out\x64\out\inc\random:1977:34
D:\GitHub\STL\out\x64\out\inc\random:1978:27: runtime error: shift exponent 64 is too large for 64-bit type '_Eres' (aka 'unsigned long long')
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior D:\GitHub\STL\out\x64\out\inc\random:1978:27
D:\GitHub\STL\out\x64\out\inc\random:1978:42: runtime error: shift exponent 64 is too large for 64-bit type '_Eres' (aka 'unsigned long long')
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior D:\GitHub\STL\out\x64\out\inc\random:1978:42
D:\GitHub\STL\out\x64\out\inc\random:1915:25: runtime error: shift exponent 64 is too large for 64-bit type 'result_type' (aka 'unsigned long long')
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior D:\GitHub\STL\out\x64\out\inc\random:1915:25
val: 14514284786278117030

test<mt19937, 64>():
D:\GitHub\STL\out\x64\out\inc\random:1977:25: runtime error: shift exponent 32 is too large for 32-bit type '_Eres' (aka 'unsigned int')
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior D:\GitHub\STL\out\x64\out\inc\random:1977:25
D:\GitHub\STL\out\x64\out\inc\random:1977:34: runtime error: shift exponent 32 is too large for 32-bit type '_Eres' (aka 'unsigned int')
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior D:\GitHub\STL\out\x64\out\inc\random:1977:34
D:\GitHub\STL\out\x64\out\inc\random:1978:27: runtime error: shift exponent 32 is too large for 32-bit type '_Eres' (aka 'unsigned int')
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior D:\GitHub\STL\out\x64\out\inc\random:1978:27
D:\GitHub\STL\out\x64\out\inc\random:1978:42: runtime error: shift exponent 32 is too large for 32-bit type '_Eres' (aka 'unsigned int')
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior D:\GitHub\STL\out\x64\out\inc\random:1978:42
val: 15028999435905310454

test<mt19937, 32>():
D:\GitHub\STL\out\x64\out\inc\random:1977:25: runtime error: shift exponent 32 is too large for 32-bit type '_Eres' (aka 'unsigned int')
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior D:\GitHub\STL\out\x64\out\inc\random:1977:25
D:\GitHub\STL\out\x64\out\inc\random:1977:34: runtime error: shift exponent 32 is too large for 32-bit type '_Eres' (aka 'unsigned int')
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior D:\GitHub\STL\out\x64\out\inc\random:1977:34
D:\GitHub\STL\out\x64\out\inc\random:1978:27: runtime error: shift exponent 32 is too large for 32-bit type '_Eres' (aka 'unsigned int')
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior D:\GitHub\STL\out\x64\out\inc\random:1978:27
D:\GitHub\STL\out\x64\out\inc\random:1978:42: runtime error: shift exponent 32 is too large for 32-bit type '_Eres' (aka 'unsigned int')
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior D:\GitHub\STL\out\x64\out\inc\random:1978:42
val: 3499211612

Affected lines:

_Yx0 = (_Rx >> _Wx0) << _Wx0;
_Yx1 = (((_Rx >> _Wx0) >> 1) << _Wx0) << 1;
_Res = _Res << _Wx0 | (static_cast<result_type>(_Val) & _Mask);

I am suspicious of this line, but I haven't found a case to trigger it yet:

_Res = _Res << (_Wx0 + 1) | (static_cast<result_type>(_Val) & _Mask);