Skip to content

Commit

Permalink
x86/cpu: Allow feature bit names from /proc/cpuinfo in clearcpuid=
Browse files Browse the repository at this point in the history
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

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
  • Loading branch information
Borislav Petkov committed Mar 29, 2022
1 parent 9c6cb3f commit c3b9dcd
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 17 deletions.
11 changes: 8 additions & 3 deletions Documentation/admin-guide/kernel-parameters.txt
Expand Up @@ -631,12 +631,17 @@
Defaults to zero when built as a module and to
10 seconds when built into the kernel.

clearcpuid=BITNUM[,BITNUM...] [X86]
clearcpuid=X[,X...] [X86]
Disable CPUID feature X for the kernel. See
arch/x86/include/asm/cpufeatures.h for the valid bit
numbers. Note the Linux specific bits are not necessarily
stable over kernel options, but the vendor specific
numbers X. Note the Linux-specific bits are not necessarily
stable over kernel options, but the vendor-specific
ones should be.
X can also be a string as appearing in the flags: line
in /proc/cpuinfo which does not have the above
instability issue. However, not all features have names
in /proc/cpuinfo.
Note that using this option will taint your kernel.
Also note that user programs calling CPUID directly
or using the feature without checking anything
will still see it. This just prevents it from
Expand Down
7 changes: 5 additions & 2 deletions arch/x86/include/asm/cpufeature.h
Expand Up @@ -34,14 +34,17 @@ enum cpuid_leafs
CPUID_8000_001F_EAX,
};

#define X86_CAP_FMT_NUM "%d:%d"
#define x86_cap_flag_num(flag) ((flag) >> 5), ((flag) & 31)

#ifdef CONFIG_X86_FEATURE_NAMES
extern const char * const x86_cap_flags[NCAPINTS*32];
extern const char * const x86_power_flags[32];
#define X86_CAP_FMT "%s"
#define x86_cap_flag(flag) x86_cap_flags[flag]
#else
#define X86_CAP_FMT "%d:%d"
#define x86_cap_flag(flag) ((flag) >> 5), ((flag) & 31)
#define X86_CAP_FMT X86_CAP_FMT_NUM
#define x86_cap_flag x86_cap_flag_num
#endif

/*
Expand Down
62 changes: 50 additions & 12 deletions arch/x86/kernel/cpu/common.c
Expand Up @@ -1368,8 +1368,8 @@ static void detect_nopl(void)
static void __init cpu_parse_early_param(void)
{
char arg[128];
char *argptr = arg;
int arglen, res, bit;
char *argptr = arg, *opt;
int arglen, taint = 0;

#ifdef CONFIG_X86_32
if (cmdline_find_option_bool(boot_command_line, "no387"))
Expand Down Expand Up @@ -1397,21 +1397,59 @@ static void __init cpu_parse_early_param(void)
return;

pr_info("Clearing CPUID bits:");
do {
res = get_option(&argptr, &bit);
if (res == 0 || res == 3)
break;

/* If the argument was too long, the last bit may be cut off */
if (res == 1 && arglen >= sizeof(arg))
break;
while (argptr) {
bool found __maybe_unused = false;
unsigned int bit;

opt = strsep(&argptr, ",");

/*
* Handle naked numbers first for feature flags which don't
* have names.
*/
if (!kstrtouint(opt, 10, &bit)) {
if (bit < NCAPINTS * 32) {

/* empty-string, i.e., ""-defined feature flags */
if (!x86_cap_flags[bit])
pr_cont(" " X86_CAP_FMT_NUM, x86_cap_flag_num(bit));
else
pr_cont(" " X86_CAP_FMT, x86_cap_flag(bit));

setup_clear_cpu_cap(bit);
taint++;
}
/*
* The assumption is that there are no feature names with only
* numbers in the name thus go to the next argument.
*/
continue;
}

#ifdef CONFIG_X86_FEATURE_NAMES
for (bit = 0; bit < 32 * NCAPINTS; bit++) {
if (!x86_cap_flag(bit))
continue;

if (bit >= 0 && bit < NCAPINTS * 32) {
pr_cont(" " X86_CAP_FMT, x86_cap_flag(bit));
if (strcmp(x86_cap_flag(bit), opt))
continue;

pr_cont(" %s", opt);
setup_clear_cpu_cap(bit);
taint++;
found = true;
break;
}
} while (res == 2);

if (!found)
pr_cont(" (unknown: %s)", opt);
#endif
}
pr_cont("\n");

if (taint)
add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK);
}

/*
Expand Down

0 comments on commit c3b9dcd

Please sign in to comment.