Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/cpu: Allow feature bit names from /proc/cpuinfo in clearcpuid=

Having to give the X86_FEATURE array indices in order to disable a
feature bit for testing is not really user-friendly. So accept the
feature bit names too.

Some feature bits don't have names so there the array indices are still
accepted, of course.

Clearing CPUID flags is not something which should be done in production
so taint the kernel too.

An exemplary cmdline would then be something like:

clearcpuid=de,440,smca,succory,bmi1,3dnow

("succory" is wrong on purpose). And it says:

[ ... ] Clearing CPUID bits: de 13:24 smca (unknown: succory) bmi1 3dnow

[ Fix CONFIG_X86_FEATURE_NAMES=n build error as reported by the 0day
robot: https://lore.kernel.org/r/202203292206.ICsY2RKX-lkp@intel.com ]

Signed-off-by: Borislav Petkov <bp@suse.de>
Reviewed-by: Kees Cook <keescook@chromium.org>
Link: https://lore.kernel.org/r/20220127115626.14179-2-bp@alien8.de

+66 -18
+8 -3
Documentation/admin-guide/kernel-parameters.txt
··· 631 631 Defaults to zero when built as a module and to 632 632 10 seconds when built into the kernel. 633 633 634 - clearcpuid=BITNUM[,BITNUM...] [X86] 634 + clearcpuid=X[,X...] [X86] 635 635 Disable CPUID feature X for the kernel. See 636 636 arch/x86/include/asm/cpufeatures.h for the valid bit 637 - numbers. Note the Linux specific bits are not necessarily 638 - stable over kernel options, but the vendor specific 637 + numbers X. Note the Linux-specific bits are not necessarily 638 + stable over kernel options, but the vendor-specific 639 639 ones should be. 640 + X can also be a string as appearing in the flags: line 641 + in /proc/cpuinfo which does not have the above 642 + instability issue. However, not all features have names 643 + in /proc/cpuinfo. 644 + Note that using this option will taint your kernel. 640 645 Also note that user programs calling CPUID directly 641 646 or using the feature without checking anything 642 647 will still see it. This just prevents it from
+5 -2
arch/x86/include/asm/cpufeature.h
··· 34 34 CPUID_8000_001F_EAX, 35 35 }; 36 36 37 + #define X86_CAP_FMT_NUM "%d:%d" 38 + #define x86_cap_flag_num(flag) ((flag) >> 5), ((flag) & 31) 39 + 37 40 #ifdef CONFIG_X86_FEATURE_NAMES 38 41 extern const char * const x86_cap_flags[NCAPINTS*32]; 39 42 extern const char * const x86_power_flags[32]; 40 43 #define X86_CAP_FMT "%s" 41 44 #define x86_cap_flag(flag) x86_cap_flags[flag] 42 45 #else 43 - #define X86_CAP_FMT "%d:%d" 44 - #define x86_cap_flag(flag) ((flag) >> 5), ((flag) & 31) 46 + #define X86_CAP_FMT X86_CAP_FMT_NUM 47 + #define x86_cap_flag x86_cap_flag_num 45 48 #endif 46 49 47 50 /*
+53 -13
arch/x86/kernel/cpu/common.c
··· 1368 1368 static void __init cpu_parse_early_param(void) 1369 1369 { 1370 1370 char arg[128]; 1371 - char *argptr = arg; 1372 - int arglen, res, bit; 1371 + char *argptr = arg, *opt; 1372 + int arglen, taint = 0; 1373 1373 1374 1374 #ifdef CONFIG_X86_32 1375 1375 if (cmdline_find_option_bool(boot_command_line, "no387")) ··· 1397 1397 return; 1398 1398 1399 1399 pr_info("Clearing CPUID bits:"); 1400 - do { 1401 - res = get_option(&argptr, &bit); 1402 - if (res == 0 || res == 3) 1403 - break; 1404 1400 1405 - /* If the argument was too long, the last bit may be cut off */ 1406 - if (res == 1 && arglen >= sizeof(arg)) 1407 - break; 1401 + while (argptr) { 1402 + bool found __maybe_unused = false; 1403 + unsigned int bit; 1408 1404 1409 - if (bit >= 0 && bit < NCAPINTS * 32) { 1410 - pr_cont(" " X86_CAP_FMT, x86_cap_flag(bit)); 1411 - setup_clear_cpu_cap(bit); 1405 + opt = strsep(&argptr, ","); 1406 + 1407 + /* 1408 + * Handle naked numbers first for feature flags which don't 1409 + * have names. 1410 + */ 1411 + if (!kstrtouint(opt, 10, &bit)) { 1412 + if (bit < NCAPINTS * 32) { 1413 + 1414 + #ifdef CONFIG_X86_FEATURE_NAMES 1415 + /* empty-string, i.e., ""-defined feature flags */ 1416 + if (!x86_cap_flags[bit]) 1417 + pr_cont(" " X86_CAP_FMT_NUM, x86_cap_flag_num(bit)); 1418 + else 1419 + #endif 1420 + pr_cont(" " X86_CAP_FMT, x86_cap_flag(bit)); 1421 + 1422 + setup_clear_cpu_cap(bit); 1423 + taint++; 1424 + } 1425 + /* 1426 + * The assumption is that there are no feature names with only 1427 + * numbers in the name thus go to the next argument. 1428 + */ 1429 + continue; 1412 1430 } 1413 - } while (res == 2); 1431 + 1432 + #ifdef CONFIG_X86_FEATURE_NAMES 1433 + for (bit = 0; bit < 32 * NCAPINTS; bit++) { 1434 + if (!x86_cap_flag(bit)) 1435 + continue; 1436 + 1437 + if (strcmp(x86_cap_flag(bit), opt)) 1438 + continue; 1439 + 1440 + pr_cont(" %s", opt); 1441 + setup_clear_cpu_cap(bit); 1442 + taint++; 1443 + found = true; 1444 + break; 1445 + } 1446 + 1447 + if (!found) 1448 + pr_cont(" (unknown: %s)", opt); 1449 + #endif 1450 + } 1414 1451 pr_cont("\n"); 1452 + 1453 + if (taint) 1454 + add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK); 1415 1455 } 1416 1456 1417 1457 /*