-
-
Notifications
You must be signed in to change notification settings - Fork 737
Description
Affected file
cosmopolitan/libc/intrin/udivmodti4.c
Relevant code
forceinline du_int udiv128by64to64default(du_int u1, du_int u0, du_int v,
du_int *r) {
const unsigned n_udword_bits = sizeof(du_int) * CHAR_BIT;
const du_int b = 1ULL << (n_udword_bits / 2);
du_int un1, un0, vn1, vn0, q1, q0, un64, un21, un10, rhat;
si_int s;
s = __builtin_clzll(v);
if (s > 0) {
v = v << s;
un64 = (u1 << s) | (u0 >> (n_udword_bits - s));
un10 = u0 << s;
} else {
un64 = u1;
un10 = u0;
}
// ... remainder of the long division algorithm ...
}Problem description
This function computes a 128-bit ÷ 64-bit unsigned division using manual normalization.
However, the line:
s = __builtin_clzll(v);can invoke undefined behavior (UB) when v == 0, because according to GCC documentation:
“If x is 0, the result of
__builtin_clz(x)is undefined.”
If the caller ever passes v == 0, this function will:
- Call
__builtin_clzll(0)→ undefined behavior, - Proceed to divide by
vn1 = v >> 32or directly byv, also undefined (division by zero).
This pattern matches the root cause of GCC Bug 101175 – "builtin_clz generates wrong bsr instruction",
where __builtin_clz(0) caused miscompilation or runtime faults due to undefined instruction selection (LZCNT/BSR) on x86 targets. the function itself provides no internal guard,
which means the undefined behavior may still be reached through unexpected inputs or optimizations.
Recommended fix
Add an explicit zero check at the start of the function, returning early or handling it consistently with the intended API contract.
For example:
forceinline du_int udiv128by64to64default(du_int u1, du_int u0, du_int v,
du_int *r) {
if (v == 0) {
// Handle division by zero according to project policy.
// e.g., set remainder to 0 and return max value, or assert.
if (r) *r = 0;
return 0; // or appropriate sentinel
}
const unsigned n_udword_bits = sizeof(du_int) * CHAR_BIT;
const du_int b = 1ULL << (n_udword_bits / 2);
si_int s = __builtin_clzll(v);
// normalization logic continues...
}This eliminates the __builtin_clzll(0) undefined behavior and prevents further invalid divisions.
Why it matters
- Calling
__builtin_clzll(0)is undefined and may lead to wrong code generation on some architectures (as seen in GCC PR101175). - A single-line guard removes both
clzll(0)and division-by-zero hazards. - The change has zero runtime cost when
v != 0(the common case).