RFR: 8365163: [ubsan] left-shift issue in globalDefinitions.hpp

Kim Barrett kbarrett at openjdk.org
Wed Aug 20 14:19:41 UTC 2025


On Wed, 20 Aug 2025 10:28:05 GMT, Afshin Zafari <azafari at openjdk.org> wrote:

>> First, we do not care about left shift of negative value on its own
>> (JDK-8240259). Indicating this issue has anything to do with that is just
>> confusing matters.
>> 
>> What we care about is signed integer overflow, which is UB. And there is
>> indeed such overflow in this code. Having the JBS issue and the PR description
>> be more direct about that would have avoided confusion on my part.
>> 
>> (I'm somewhat surprised none of our compilers just breaks this obvious
>> overflow. Or maybe they do and we've not noticed, though that seems unlikely.)
>> 
>> As @theRealAph points out, the proposed change still contains overflows. For example
>> 
>> 1061:  *value &= (jlong)0xffffffff << 32;
>> 
>> 
>> But this can be vastly simplified to just
>> 
>> inline jlong jlong_from(jint h, jint l) {
>>   // First cast jint values to juint, so cast to julong will zero-extend.
>>   julong high = (julong)(juint)h << 32;
>>   julong low = (julong)(juint)l;
>>   return (jlong)(high | low);
>> }
>> 
>> and delete the now unused set_high and set_low functions. (There are set_high
>> and set_low functions in sharedRuntimeMath.hpp, but they deal with double
>> rather than jlong.)
>
> I am aware of not caring the issues of 'left-shift of negative values' since after C++20 they will not be UB anymore. I use UBSAN left-shift checks for finding other hidden/potential problems around left-shifts.
> 
> Sorry if missing enough explanation in title and/or description parts brought you confusion.
> 
> AFAIU, the code `(jlong)0xffffffff << 32` is not problematic since `jlong` is 64 bits and `0xffffffff` is `unsigned int` then casting to `jlong` makes it as `0x00000000ffffffff` and shifting it 32 bits to left results in `0xffffffff00000000` which means the the result is still representable with no loss. What am I missing here?
> 
> So, if my understanding above is correct, there will be no issue in these two functions since left-shift o f negative values are to be ignored for now. IOW, we can close this PR without integration.

`(jlong)0xffffffff << 32` is a (obvious) signed integer overflow. It's a
signed shift, and the sign bit changes.

However, there was a subtle but intentional change in C++14 that permits this
specific overflow case. A signed left shift that moves a 1 bit into the sign
bit position (but not beyond!) is permitted. The actual wording is

C++14 5.8/2
"[...] if E1 has a signed type and non-negative value, and E1×2E2 is
representable in the corresponding unsigned type of the result type, then that
value, converted to the result type, is the resulting value; [...]"

C++11 5.8/2
"[...] if E1 has a signed type and non-negative value, and E1×2E2 is representable
in the result type, then that is the resulting value; [...]"

So that code has an overflow that is UB in C++11 and earlier, but it's not UB
in C++14 and later.

That's all very complicated and subtle analysis. I think my proposed
alternative skips all, is very straightforward to understand, and insulates us
from any future explorations of the sort that led us here.

-------------

PR Review Comment: https://git.openjdk.org/jdk/pull/26809#discussion_r2288334648


More information about the hotspot-dev mailing list