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-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250312143738.458507-15-darwi@linutronix.de>
Date: Wed, 12 Mar 2025 15:37:31 +0100
From: "Ahmed S. Darwish" <darwi@...utronix.de>
To: Ingo Molnar <mingo@...hat.com>,
	Dave Hansen <dave.hansen@...ux.intel.com>,
	Borislav Petkov <bp@...en8.de>
Cc: Thomas Gleixner <tglx@...utronix.de>,
	Andrew Cooper <andrew.cooper3@...rix.com>,
	"H. Peter Anvin" <hpa@...or.com>,
	John Ogness <john.ogness@...utronix.de>,
	x86@...nel.org,
	x86-cpuid@...ts.linux.dev,
	LKML <linux-kernel@...r.kernel.org>,
	"Ahmed S. Darwish" <darwi@...utronix.de>
Subject: [PATCH v2 14/20] tools/x86/kcpuid: Filter valid CPUID ranges

Next commits will introduce vendor-specific CPUID ranges like Transmeta's
0x8086000 range and Centaur's 0xc0000000.

Initially explicit vendor detection was implemented, but it turned out to
be not strictly necessary.  As Dave Hansen noted, even established tools
like cpuid(1) just tries all ranges indices, and see if the CPU responds
back with something sensible.

Do something similar at setup_cpuid_range().  Query the range's index,
and check the maximum range function value returned.  If it's within an
expected interval of [range_index, range_index + MAX_RANGE_INDEX_OFFSET],
accept the range as valid and further query its leaves.

Set MAX_RANGE_INDEX_OFFSET to a heuristic of 0xff.  That should be
sensible enough since all the ranges covered by x86-cpuid-db XML database
are:

	0x00000000	0x00000023
	0x40000000	0x40000000
	0x80000000	0x80000026
	0x80860000	0x80860007
	0xc0000000	0xc0000001

At setup_cpuid_range(), if the range's returned maximum function was not
sane, mark it as invalid by setting its number of leaves, range->nr, to
zero.

Introduce the for_each_valid_cpuid_range() iterator instead of sprinkling
"range->nr != 0" checks throughout the code.

Suggested-by: Dave Hansen <dave.hansen@...el.com>
Signed-off-by: Ahmed S. Darwish <darwi@...utronix.de>
---
 tools/arch/x86/kcpuid/kcpuid.c | 38 +++++++++++++++++++++++++---------
 1 file changed, 28 insertions(+), 10 deletions(-)

diff --git a/tools/arch/x86/kcpuid/kcpuid.c b/tools/arch/x86/kcpuid/kcpuid.c
index fff6db1119ed..94a5926d00d0 100644
--- a/tools/arch/x86/kcpuid/kcpuid.c
+++ b/tools/arch/x86/kcpuid/kcpuid.c
@@ -96,8 +96,13 @@ static char *range_to_str(struct cpuid_range *range)
 	}
 }
 
-#define for_each_cpuid_range(range)		\
-	for (unsigned int i = 0; i < ARRAY_SIZE(ranges) && ((range) = &ranges[i]); i++)
+#define __for_each_cpuid_range(range, __condition)				\
+	for (unsigned int i = 0;						\
+	     i < ARRAY_SIZE(ranges) && ((range) = &ranges[i]) && (__condition);	\
+	     i++)
+
+#define for_each_valid_cpuid_range(range)	__for_each_cpuid_range(range, (range)->nr != 0)
+#define for_each_cpuid_range(range)		__for_each_cpuid_range(range, true)
 
 struct cpuid_range *index_to_cpuid_range(u32 index)
 {
@@ -105,7 +110,7 @@ struct cpuid_range *index_to_cpuid_range(u32 index)
 	u32 range_idx = index & CPUID_INDEX_MASK;
 	struct cpuid_range *range;
 
-	for_each_cpuid_range(range) {
+	for_each_valid_cpuid_range(range) {
 		if (range->index == range_idx && (u32)range->nr > func_idx)
 			return range;
 	}
@@ -223,20 +228,32 @@ static void raw_dump_range(struct cpuid_range *range)
 }
 
 #define MAX_SUBLEAF_NUM		64
+#define MAX_RANGE_INDEX_OFFSET	0xff
 void setup_cpuid_range(struct cpuid_range *range)
 {
-	u32 max_func, idx_func;
+	u32 max_func, range_funcs_sz;
 	u32 eax, ebx, ecx, edx;
 
 	cpuid(range->index, max_func, ebx, ecx, edx);
 
-	idx_func = (max_func & CPUID_FUNCTION_MASK) + 1;
-	range->funcs = malloc(sizeof(struct cpuid_func) * idx_func);
+	/*
+	 * If the CPUID range's maximum function value is garbage, then it
+	 * is not recognized by this CPU.  Set the range's number of valid
+	 * leaves to zero so that for_each_valid_cpu_range() can ignore it.
+	 */
+	if (max_func < range->index || max_func > (range->index + MAX_RANGE_INDEX_OFFSET)) {
+		range->nr = 0;
+		return;
+	}
+
+	range->nr = (max_func & CPUID_FUNCTION_MASK) + 1;
+	range_funcs_sz = range->nr * sizeof(struct cpuid_func);
+
+	range->funcs = malloc(range_funcs_sz);
 	if (!range->funcs)
 		err(EXIT_FAILURE, NULL);
 
-	range->nr = idx_func;
-	memset(range->funcs, 0, sizeof(struct cpuid_func) * idx_func);
+	memset(range->funcs, 0, range_funcs_sz);
 
 	for (u32 f = range->index; f <= max_func; f++) {
 		u32 max_subleaf = MAX_SUBLEAF_NUM;
@@ -342,6 +359,7 @@ static void parse_line(char *line)
 		return;
 
 	/* Skip line parsing if the index CPUID output is all zero */
+	index &= CPUID_FUNCTION_MASK;
 	func = &range->funcs[index];
 	if (!func->nr)
 		return;
@@ -522,7 +540,7 @@ static void show_info(void)
 
 	if (show_raw) {
 		/* Show all of the raw output of 'cpuid' instr */
-		for_each_cpuid_range(range)
+		for_each_valid_cpuid_range(range)
 			raw_dump_range(range);
 		return;
 	}
@@ -551,7 +569,7 @@ static void show_info(void)
 	}
 
 	printf("CPU features:\n=============\n\n");
-	for_each_cpuid_range(range)
+	for_each_valid_cpuid_range(range)
 		show_range(range);
 }
 
-- 
2.48.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ