[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <1AD55673-B7F4-4DB7-AE80-1AC81709F65A@gmail.com>
Date: Thu, 18 Sep 2025 09:50:52 +0800
From: 陈华昭(Lyican) <lyican53@...il.com>
To: ceph-devel@...r.kernel.org
Cc: idryomov@...il.com,
xiubli@...hat.com,
linux-kernel@...r.kernel.org,
Slava.Dubeyko@....com
Subject: [PATCH] ceph: Fix potential undefined behavior in crush_ln() with GCC
11.1.0
When compiled with GCC 11.1.0 and -march=x86-64-v3 -O1 optimization flags,
__builtin_clz() may generate BSR instructions without proper zero handling.
The BSR instruction has undefined behavior when the source operand is zero,
which could occur when (x & 0x1FFFF) equals 0 in the crush_ln() function.
This issue is documented in GCC bug 101175:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101175
The problematic code path occurs in crush_ln() when:
- x is incremented from xin
- (x & 0x18000) == 0 (condition for the optimization)
- (x & 0x1FFFF) == 0 (zero argument to __builtin_clz)
Testing with GCC 11.5.0 confirms that specific input values like 0x7FFFF,
0x9FFFF, 0xBFFFF, 0xDFFFF, 0xFFFFF can trigger this condition, causing
__builtin_clz(0) to be called with undefined behavior.
Add a zero check before calling __builtin_clz() to ensure defined behavior
across all GCC versions and optimization levels.
Signed-off-by: Huazhao Chen <lyican53@...il.com>
---
net/ceph/crush/mapper.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/net/ceph/crush/mapper.c b/net/ceph/crush/mapper.c
index 1234567..abcdef0 100644
--- a/net/ceph/crush/mapper.c
+++ b/net/ceph/crush/mapper.c
@@ -262,7 +262,8 @@ static __u64 crush_ln(unsigned int xin)
* do it in one step instead of iteratively
*/
if (!(x & 0x18000)) {
- int bits = __builtin_clz(x & 0x1FFFF) - 16;
+ u32 masked = x & 0x1FFFF;
+ int bits = masked ? __builtin_clz(masked) - 16 : 16;
x <<= bits;
iexpon = 15 - bits;
}
--
2.40.1
Testing:
=======
The issue can be verified with the following test case that identifies
problematic input values:
```c
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
/* Simplified version showing the problematic pattern */
static void test_crush_ln_bug(void)
{
unsigned int problematic_inputs[] = {
0x7FFFF, 0x9FFFF, 0xBFFFF, 0xDFFFF, 0xFFFFF
};
printf("Testing inputs that trigger __builtin_clz(0):\n");
for (int i = 0; i < 5; i++) {
unsigned int input = problematic_inputs[i];
unsigned int x = input + 1;
if (!(x & 0x18000)) {
unsigned int masked = x & 0x1FFFF;
printf("Input 0x%06X: x+1=0x%06X, masked=0x%05X %s\n",
input, x, masked,
masked == 0 ? "- BUG! Zero to __builtin_clz" : "- Safe");
}
}
}
```
This test confirms that all five input values result in __builtin_clz(0)
being called, demonstrating the need for the zero check in the fix.
The fix ensures that when masked == 0, we use the appropriate default value
(16) instead of calling __builtin_clz(0), maintaining the same mathematical
behavior while avoiding undefined compiler behavior.
Powered by blists - more mailing lists