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 for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <20260121103204.48713-1-david.desobry@formalgen.com>
Date: Wed, 21 Jan 2026 11:32:04 +0100
From: David Desobry <david.desobry@...malgen.com>
To: tglx@...nel.org,
	mingo@...hat.com,
	bp@...en8.de,
	dave.hansen@...ux.intel.com
Cc: x86@...nel.org,
	hpa@...or.com,
	linux-kernel@...r.kernel.org,
	David Desobry <david.desobry@...malgen.com>
Subject: [PATCH v5] x86/lib: Rename num_digits() to num_digits_u32() and optimize

The current implementation of num_digits() uses a loop with repeated
multiplication, which is inefficient. Furthermore, it takes a signed
integer which leads to undefined behavior when negating INT_MIN.

Since this function is only used for unsigned magnitudes, change it
to num_digits_u32() and convert the interface to take an unsigned int.
This naturally resolves the INT_MIN overflow issue and clarifies the
function's intended use.

Replace the loop with a branchless sequence using inline assembly. By
using the 'sbb' instruction against the carry flag after comparisons,
we eliminate all conditional branches. This provides constant-time
performance and avoids CPU branch misprediction penalties.

Update all existing callers and the function comment to reflect the
new name and the fact that signs are no longer handled.

Signed-off-by: David Desobry <david.desobry@...malgen.com>
---
v5:
 - Changed name to num_digits_u32() and updated all callers to avoid 
   semantic confusion
v4:
 - Switched to branchless inline assembly.
 - Changed function signature to unsigned int.
 - Updated prototype in arch/x86/include/asm/misc.h.

 arch/x86/include/asm/misc.h |  2 +-
 arch/x86/kernel/smpboot.c   |  8 ++++----
 arch/x86/lib/misc.c         | 28 +++++++++++++---------------
 3 files changed, 18 insertions(+), 20 deletions(-)

diff --git a/arch/x86/include/asm/misc.h b/arch/x86/include/asm/misc.h
index bb049cca3729..78d96b81d985 100644
--- a/arch/x86/include/asm/misc.h
+++ b/arch/x86/include/asm/misc.h
@@ -2,6 +2,6 @@
 #ifndef _ASM_X86_MISC_H
 #define _ASM_X86_MISC_H
 
-int num_digits(int val);
+int num_digits_u32(unsigned int x);
 
 #endif /* _ASM_X86_MISC_H */
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index 5cd6950ab672..fc92f04c7f04 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -849,10 +849,10 @@ static void announce_cpu(int cpu, int apicid)
 	int node = early_cpu_to_node(cpu);
 
 	if (!width)
-		width = num_digits(num_possible_cpus()) + 1; /* + '#' sign */
+		width = num_digits_u32(num_possible_cpus()) + 1; /* + '#' sign */
 
 	if (!node_width)
-		node_width = num_digits(num_possible_nodes()) + 1; /* + '#' */
+		node_width = num_digits_u32(num_possible_nodes()) + 1; /* + '#' */
 
 	if (system_state < SYSTEM_RUNNING) {
 		if (first)
@@ -864,7 +864,7 @@ static void announce_cpu(int cpu, int apicid)
 			current_node = node;
 
 			printk(KERN_INFO ".... node %*s#%d, CPUs:  ",
-			       node_width - num_digits(node), " ", node);
+			       node_width - num_digits_u32(node), " ", node);
 		}
 
 		/* Add padding for the BSP */
@@ -872,7 +872,7 @@ static void announce_cpu(int cpu, int apicid)
 			pr_cont("%*s", width + 1, " ");
 		first = 0;
 
-		pr_cont("%*s#%d", width - num_digits(cpu), " ", cpu);
+		pr_cont("%*s#%d", width - num_digits_u32(cpu), " ", cpu);
 	} else
 		pr_info("Booting Node %d Processor %d APIC 0x%x\n",
 			node, cpu, apicid);
diff --git a/arch/x86/lib/misc.c b/arch/x86/lib/misc.c
index 40b81c338ae5..b21ca6f0dbf8 100644
--- a/arch/x86/lib/misc.c
+++ b/arch/x86/lib/misc.c
@@ -2,23 +2,21 @@
 #include <asm/misc.h>
 
 /*
- * Count the digits of @val including a possible sign.
- *
- * (Typed on and submitted from hpa's mobile phone.)
+ * Count the decimal digits of an unsigned integer.
  */
-int num_digits(int val)
+int num_digits_u32(unsigned int x)
 {
-	long long m = 10;
-	int d = 1;
+	int n = 0;
 
-	if (val < 0) {
-		d++;
-		val = -val;
-	}
+	asm("cmp %2,%1; sbb $-2,%0" : "+r" (n) : "r" (x), "g" (10));
+	asm("cmp %2,%1; sbb $-1,%0" : "+r" (n) : "r" (x), "g" (100));
+	asm("cmp %2,%1; sbb $-1,%0" : "+r" (n) : "r" (x), "g" (1000));
+	asm("cmp %2,%1; sbb $-1,%0" : "+r" (n) : "r" (x), "g" (10000));
+	asm("cmp %2,%1; sbb $-1,%0" : "+r" (n) : "r" (x), "g" (100000));
+	asm("cmp %2,%1; sbb $-1,%0" : "+r" (n) : "r" (x), "g" (1000000));
+	asm("cmp %2,%1; sbb $-1,%0" : "+r" (n) : "r" (x), "g" (10000000));
+	asm("cmp %2,%1; sbb $-1,%0" : "+r" (n) : "r" (x), "g" (100000000));
+	asm("cmp %2,%1; sbb $-1,%0" : "+r" (n) : "r" (x), "g" (1000000000));
 
-	while (val >= m) {
-		m *= 10;
-		d++;
-	}
-	return d;
+	return n;
 }
-- 
2.43.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ