lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<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

Powered by Openwall GNU/*/Linux Powered by OpenVZ