🛡️

【Security】Spectulative Store Bypass for x86

2024/10/25に公開

General Information

  • Name: Speculative Store Bypass (SSB), Spectre-V4, Variant 4, Spectre Next Generation, Spectre-NG
  • CVE: CVE-2018-3639, INTEL-SA-00115
  • Disclosure Date: 2018-03-21

Latest Kernel Code (v6.11.4)

Sysfs

  • Access to /sys/devices/system/cpu/vulnerabilities/spec_store_bypass is handled by cpu_show_spec_store_bypass().
  • It returns one of the following four values depending on ssb_mode value:
    • ssb_mode == SPEC_STORE_BYPASS_NONE => "Vulnerable"
    • ssb_mode == SPEC_STORE_BYPASS_DISABLE => "Mitigation: Speculative Store Bypass disabled"
    • ssb_mode == SPEC_STORE_BYPASS_PRCTL => "Mitigation: Speculative Store Bypass disabled via prctl"
    • ssb_mode == SPEC_STORE_BYPASS_SECCOMP => "Mitigation: Speculative Store Bypass disabled via prctl and seccomp"

cpu_show_spec_store_bypass()

https://elixir.bootlin.com/linux/v6.11.4/source/drivers/base/cpu.c#L606

static DEVICE_ATTR(spec_store_bypass, 0444, cpu_show_spec_store_bypass, NULL);

The sysfs file is read only and handled by cpu_show_spec_store_bypass().

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/kernel/cpu/bugs.c#L2996

ssize_t cpu_show_spec_store_bypass(struct device *dev, struct device_attribute *attr, char *buf)
{
	return cpu_show_common(dev, attr, buf, X86_BUG_SPEC_STORE_BYPASS);
}

cpu_show_common()

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/kernel/cpu/bugs.c#L2916

static ssize_t cpu_show_common(struct device *dev, struct device_attribute *attr,
			       char *buf, unsigned int bug)
{
	if (!boot_cpu_has_bug(bug))
		return sysfs_emit(buf, "Not affected\n");

	switch (bug) {
// snipped
	case X86_BUG_SPEC_STORE_BYPASS:
		return sysfs_emit(buf, "%s\n", ssb_strings[ssb_mode]);
// snipped
	}

	return sysfs_emit(buf, "Vulnerable\n");
}

ssb_strings[]

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/kernel/cpu/bugs.c#L2021

static const char * const ssb_strings[] = {
	[SPEC_STORE_BYPASS_NONE]	= "Vulnerable",
	[SPEC_STORE_BYPASS_DISABLE]	= "Mitigation: Speculative Store Bypass disabled",
	[SPEC_STORE_BYPASS_PRCTL]	= "Mitigation: Speculative Store Bypass disabled via prctl",
	[SPEC_STORE_BYPASS_SECCOMP]	= "Mitigation: Speculative Store Bypass disabled via prctl and seccomp",
};

enum ssb_mitigation

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/include/asm/nospec-branch.h#L513

/* The Speculative Store Bypass disable variants */
enum ssb_mitigation {
	SPEC_STORE_BYPASS_NONE,
	SPEC_STORE_BYPASS_DISABLE,
	SPEC_STORE_BYPASS_PRCTL,
	SPEC_STORE_BYPASS_SECCOMP,
};

Vulnerability Detection

Summary

  • The processor is considered vulnerable if all the following conditions are met:
    • It is not listed in the whitelist.
    • SSB_NO is not set
      • Intel: IA32_ARCH_CAPABILITIES.SSB_NO[bit 4]
      • AMD: CPUID.80000008h:EBX.SSB_NO[bit 26]

X86_BUG_SPEC_STORE_BYPASS

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/include/asm/cpufeatures.h#L506

#define X86_BUG_SPEC_STORE_BYPASS	X86_BUG(17) /* "spec_store_bypass" CPU is affected by speculative store bypass attack */

cpu_set_bug_bits()

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/kernel/cpu/common.c#L1340

static void __init cpu_set_bug_bits(struct cpuinfo_x86 *c)
{
	u64 x86_arch_cap_msr = x86_read_arch_cap_msr();
// snipped
	if (!cpu_matches(cpu_vuln_whitelist, NO_SSB) &&
	    !(x86_arch_cap_msr & ARCH_CAP_SSB_NO) &&
	   !cpu_has(c, X86_FEATURE_AMD_SSB_NO))
		setup_force_cpu_bug(X86_BUG_SPEC_STORE_BYPASS);
// snipped
}

cpu_vuln_whitelist[] / NO_SSB

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/kernel/cpu/common.c#L1139

static const __initconst struct x86_cpu_id cpu_vuln_whitelist[] = {
// snipped
	VULNWL_INTEL(INTEL_ATOM_SILVERMONT,	NO_SSB | NO_L1TF | MSBDS_ONLY | NO_SWAPGS | NO_ITLB_MULTIHIT),
	VULNWL_INTEL(INTEL_ATOM_SILVERMONT_D,	NO_SSB | NO_L1TF | MSBDS_ONLY | NO_SWAPGS | NO_ITLB_MULTIHIT),
	VULNWL_INTEL(INTEL_ATOM_SILVERMONT_MID,	NO_SSB | NO_L1TF | MSBDS_ONLY | NO_SWAPGS | NO_ITLB_MULTIHIT),
	VULNWL_INTEL(INTEL_ATOM_AIRMONT,	NO_SSB | NO_L1TF | MSBDS_ONLY | NO_SWAPGS | NO_ITLB_MULTIHIT),
	VULNWL_INTEL(INTEL_XEON_PHI_KNL,	NO_SSB | NO_L1TF | MSBDS_ONLY | NO_SWAPGS | NO_ITLB_MULTIHIT),
	VULNWL_INTEL(INTEL_XEON_PHI_KNM,	NO_SSB | NO_L1TF | MSBDS_ONLY | NO_SWAPGS | NO_ITLB_MULTIHIT),

	VULNWL_INTEL(INTEL_CORE_YONAH,		NO_SSB),

	VULNWL_INTEL(INTEL_ATOM_AIRMONT_MID,	NO_SSB | NO_L1TF | NO_SWAPGS | NO_ITLB_MULTIHIT | MSBDS_ONLY),
	VULNWL_INTEL(INTEL_ATOM_AIRMONT_NP,	NO_SSB | NO_L1TF | NO_SWAPGS | NO_ITLB_MULTIHIT),
// snipped
	/* AMD Family 0xf - 0x12 */
	VULNWL_AMD(0x0f,	NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS | NO_SWAPGS | NO_ITLB_MULTIHIT | NO_MMIO | NO_BHI),
	VULNWL_AMD(0x10,	NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS | NO_SWAPGS | NO_ITLB_MULTIHIT | NO_MMIO | NO_BHI),
	VULNWL_AMD(0x11,	NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS | NO_SWAPGS | NO_ITLB_MULTIHIT | NO_MMIO | NO_BHI),
	VULNWL_AMD(0x12,	NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS | NO_SWAPGS | NO_ITLB_MULTIHIT | NO_MMIO | NO_BHI),
// snipped
};

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/kernel/cpu/common.c#L1116

#define NO_SSB			BIT(2)

ARCH_CAP_SSB_NO / X86_FEATURE_AMD_SSB_NO

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/include/asm/msr-index.h#L116

#define MSR_IA32_ARCH_CAPABILITIES	0x0000010a
// snipped
#define ARCH_CAP_SSB_NO			BIT(4)	/*
						 * Not susceptible to Speculative Store Bypass
						 * attack, so no Speculative Store Bypass
						 * control required.
						 */

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/include/asm/cpufeatures.h#L347

/* AMD-defined CPU features, CPUID level 0x80000008 (EBX), word 13 */
// snipped
#define X86_FEATURE_AMD_SSB_NO		(13*32+26) /* Speculative Store Bypass is fixed in hardware. */

Mitigation Detection

Summary

  • SSBD (Speculative Store Bypass Disable) is a feature to disable speculation.
  • It is enumerated on:
    • Intel: CPUID.(EAX=7h,ECX=0):EDX.SSBD[bit 31]
    • AMD: CPUID.80000008:EBX.SSBD[bit 24] / CPUID.80000008:EBX.VIRT_SSBD[bit 25] for virtualized guests
      • Some AMD families a different mechanism using LS_CFG MSR (address 0xC0011020) and the bit that should be used depends on the family.

init_speculation_control()

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/kernel/cpu/common.c#L931

static void init_speculation_control(struct cpuinfo_x86 *c)
{
// snipped
	if (cpu_has(c, X86_FEATURE_SPEC_CTRL_SSBD) ||
	    cpu_has(c, X86_FEATURE_VIRT_SSBD))
		set_cpu_cap(c, X86_FEATURE_SSBD);
// snipped
	if (cpu_has(c, X86_FEATURE_AMD_SSBD)) {
		set_cpu_cap(c, X86_FEATURE_SSBD);
		set_cpu_cap(c, X86_FEATURE_MSR_SPEC_CTRL);
		clear_cpu_cap(c, X86_FEATURE_VIRT_SSBD);
	}
}

X86_FEATURE_SPEC_CTRL_SSBD / X86_FEATURE_VIRT_SSBD / X86_FEATURE_AMD_SSBD

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/include/asm/cpufeatures.h#L440

/* Intel-defined CPU features, CPUID level 0x00000007:0 (EDX), word 18 */
// snipped
#define X86_FEATURE_SPEC_CTRL_SSBD	(18*32+31) /* Speculative Store Bypass Disable */

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/include/asm/cpufeatures.h#L346

/* AMD-defined CPU features, CPUID level 0x80000008 (EBX), word 13 */
// snipped
#define X86_FEATURE_AMD_SSBD		(13*32+24) /* Speculative Store Bypass Disable */
#define X86_FEATURE_VIRT_SSBD		(13*32+25) /* "virt_ssbd" Virtualized Speculative Store Bypass Disable */

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/include/asm/cpufeatures.h#L345-L346

/* AMD-defined CPU features, CPUID level 0x80000008 (EBX), word 13 */
#define X86_FEATURE_CLZERO		(13*32+ 0) /* "clzero" CLZERO instruction */
#define X86_FEATURE_IRPERF		(13*32+ 1) /* "irperf" Instructions Retired Count */
#define X86_FEATURE_XSAVEERPTR		(13*32+ 2) /* "xsaveerptr" Always save/restore FP error pointers */
#define X86_FEATURE_RDPRU		(13*32+ 4) /* "rdpru" Read processor register at user level */
#define X86_FEATURE_WBNOINVD		(13*32+ 9) /* "wbnoinvd" WBNOINVD instruction */
#define X86_FEATURE_AMD_IBPB		(13*32+12) /* Indirect Branch Prediction Barrier */
#define X86_FEATURE_AMD_IBRS		(13*32+14) /* Indirect Branch Restricted Speculation */
#define X86_FEATURE_AMD_STIBP		(13*32+15) /* Single Thread Indirect Branch Predictors */
#define X86_FEATURE_AMD_STIBP_ALWAYS_ON	(13*32+17) /* Single Thread Indirect Branch Predictors always-on preferred */
#define X86_FEATURE_AMD_PPIN		(13*32+23) /* "amd_ppin" Protected Processor Inventory Number */
#define X86_FEATURE_AMD_SSBD		(13*32+24) /* Speculative Store Bypass Disable */
#define X86_FEATURE_VIRT_SSBD		(13*32+25) /* "virt_ssbd" Virtualized Speculative Store Bypass Disable */

X86_FEATURE_LS_CFG_SSBD

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/include/asm/cpufeatures.h#L506

#define X86_FEATURE_LS_CFG_SSBD		( 7*32+24)  /* AMD SSBD implementation via LS_CFG MSR */

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/kernel/cpu/amd.c#L419

static void bsp_init_amd(struct cpuinfo_x86 *c)
{
// snipped
	if (!boot_cpu_has(X86_FEATURE_AMD_SSBD) &&
	    !boot_cpu_has(X86_FEATURE_VIRT_SSBD) &&
	    c->x86 >= 0x15 && c->x86 <= 0x17) {
		unsigned int bit;

		switch (c->x86) {
		case 0x15: bit = 54; break;
		case 0x16: bit = 33; break;
		case 0x17: bit = 10; break;
		default: return;
		}
		/*
		 * Try to cache the base value so further operations can
		 * avoid RMW. If that faults, do not enable SSBD.
		 */
		if (!rdmsrl_safe(MSR_AMD64_LS_CFG, &x86_amd_ls_cfg_base)) {
			setup_force_cpu_cap(X86_FEATURE_LS_CFG_SSBD);
			setup_force_cpu_cap(X86_FEATURE_SSBD);
			x86_amd_ls_cfg_ssbd_mask = 1ULL << bit;
		}
	}
// snipped
}

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/include/asm/msr-index.h#L594

#define MSR_AMD64_LS_CFG		0xc0011020

Mitigation Selection

  • Kernel parameter spec_store_bypass_disable= can take one of the following five values:
    • "on": Unconditionally disable speculative store bypass
    • "off": Unconditionally permits speculative store bypass
    • "prctl": Allows userspace to disable speculative store bypass via prctl()
    • "seccomp": All seccomp threads disable speculative store bypass in addition to prctl().
    • "auto" (default): The kernel detects whether the processor is affected and which mitigation to apply.
  • Mitigation is selected as follows:
    • If SSBD is not supported => SPEC_STORE_BYPASS_NONE
    • If the processor is not affected and spec_store_bypass_disable=off or spec_store_bypass_disable=auto => SPEC_STORE_BYPASS_NONE
    • If spec_store_bypass_disable=seccomp and CONFIG_SECCOMP is enabled => SPEC_STORE_BYPASS_SECCOMP
    • If spec_store_bypass_disable=seccomp and CONFIG_SECCOMP is not enabled => SPEC_STORE_BYPASS_PRCTL
    • If spec_store_bypass_disable=on => SPEC_STORE_BYPASS_DISABLE
    • If spec_store_bypass_disable=auto or spec_store_bypass_disable=prctl => SPEC_STORE_BYPASS_PRCTL
    • If spec_store_bypass_disable=off => SPEC_STORE_BYPASS_NONE
  • Mitigation application
    • Intel: IA32_SPEC_CTRL.SSBD[bit 2] = 1
    • AMD: IA32_SPEC_CTRL.SSBD[bit 2] = 1
      • Some AMD families use LS_CFG MSR (address 0xC0011020) and the bit that should be used depends on the family.
      • Some virtualized AMD guests use VIRT_SPEC_CTRL MSR (address 0xC001011F).

ssb_mitigation_options[]

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/kernel/cpu/bugs.c#L2028

static const struct {
	const char *option;
	enum ssb_mitigation_cmd cmd;
} ssb_mitigation_options[]  __initconst = {
	{ "auto",	SPEC_STORE_BYPASS_CMD_AUTO },    /* Platform decides */
	{ "on",		SPEC_STORE_BYPASS_CMD_ON },      /* Disable Speculative Store Bypass */
	{ "off",	SPEC_STORE_BYPASS_CMD_NONE },    /* Don't touch Speculative Store Bypass */
	{ "prctl",	SPEC_STORE_BYPASS_CMD_PRCTL },   /* Disable Speculative Store Bypass via prctl */
	{ "seccomp",	SPEC_STORE_BYPASS_CMD_SECCOMP }, /* Disable Speculative Store Bypass via prctl and seccomp */
};

https://elixir.bootlin.com/linux/v6.11.5/source/Documentation/admin-guide/kernel-parameters.txt#L6251-L6301

	spec_store_bypass_disable=
			[HW,EARLY] Control Speculative Store Bypass (SSB) Disable mitigation
			(Speculative Store Bypass vulnerability)

			Certain CPUs are vulnerable to an exploit against a
			a common industry wide performance optimization known
			as "Speculative Store Bypass" in which recent stores
			to the same memory location may not be observed by
			later loads during speculative execution. The idea
			is that such stores are unlikely and that they can
			be detected prior to instruction retirement at the
			end of a particular speculation execution window.

			In vulnerable processors, the speculatively forwarded
			store can be used in a cache side channel attack, for
			example to read memory to which the attacker does not
			directly have access (e.g. inside sandboxed code).

			This parameter controls whether the Speculative Store
			Bypass optimization is used.

			On x86 the options are:

			on      - Unconditionally disable Speculative Store Bypass
			off     - Unconditionally enable Speculative Store Bypass
			auto    - Kernel detects whether the CPU model contains an
				  implementation of Speculative Store Bypass and
				  picks the most appropriate mitigation. If the
				  CPU is not vulnerable, "off" is selected. If the
				  CPU is vulnerable the default mitigation is
				  architecture and Kconfig dependent. See below.
			prctl   - Control Speculative Store Bypass per thread
				  via prctl. Speculative Store Bypass is enabled
				  for a process by default. The state of the control
				  is inherited on fork.
			seccomp - Same as "prctl" above, but all seccomp threads
				  will disable SSB unless they explicitly opt out.

			Default mitigations:
			X86:	"prctl"

			On powerpc the options are:

			on,auto - On Power8 and Power9 insert a store-forwarding
				  barrier on kernel entry and exit. On Power7
				  perform a software flush on kernel entry and
				  exit.
			off	- No action.

			Not specifying this option is equivalent to
			spec_store_bypass_disable=auto.

ssb_parse_cmdline()

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/kernel/cpu/bugs.c#L2039

static enum ssb_mitigation_cmd __init ssb_parse_cmdline(void)
{
	enum ssb_mitigation_cmd cmd = SPEC_STORE_BYPASS_CMD_AUTO;
	char arg[20];
	int ret, i;

	if (cmdline_find_option_bool(boot_command_line, "nospec_store_bypass_disable") ||
	    cpu_mitigations_off()) {
		return SPEC_STORE_BYPASS_CMD_NONE;
	} else {
		ret = cmdline_find_option(boot_command_line, "spec_store_bypass_disable",
					  arg, sizeof(arg));
		if (ret < 0)
			return SPEC_STORE_BYPASS_CMD_AUTO;

		for (i = 0; i < ARRAY_SIZE(ssb_mitigation_options); i++) {
			if (!match_option(arg, ret, ssb_mitigation_options[i].option))
				continue;

			cmd = ssb_mitigation_options[i].cmd;
			break;
		}

		if (i >= ARRAY_SIZE(ssb_mitigation_options)) {
			pr_err("unknown option (%s). Switching to AUTO select\n", arg);
			return SPEC_STORE_BYPASS_CMD_AUTO;
		}
	}

	return cmd;
}

ssb_select_mitigation()

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/kernel/cpu/bugs.c#L2131

static void ssb_select_mitigation(void)
{
	ssb_mode = __ssb_select_mitigation();

	if (boot_cpu_has_bug(X86_BUG_SPEC_STORE_BYPASS))
		pr_info("%s\n", ssb_strings[ssb_mode]);
}

enum ssb_mitigation

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/include/asm/nospec-branch.h#L513

/* The Speculative Store Bypass disable variants */
enum ssb_mitigation {
	SPEC_STORE_BYPASS_NONE,
	SPEC_STORE_BYPASS_DISABLE,
	SPEC_STORE_BYPASS_PRCTL,
	SPEC_STORE_BYPASS_SECCOMP,
};

__ssb_select_mitigation()

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/kernel/cpu/bugs.c#L2071

static enum ssb_mitigation __init __ssb_select_mitigation(void)
{
	enum ssb_mitigation mode = SPEC_STORE_BYPASS_NONE;
	enum ssb_mitigation_cmd cmd;

	if (!boot_cpu_has(X86_FEATURE_SSBD))
		return mode;

	cmd = ssb_parse_cmdline();
	if (!boot_cpu_has_bug(X86_BUG_SPEC_STORE_BYPASS) &&
	    (cmd == SPEC_STORE_BYPASS_CMD_NONE ||
	     cmd == SPEC_STORE_BYPASS_CMD_AUTO))
		return mode;

	switch (cmd) {
	case SPEC_STORE_BYPASS_CMD_SECCOMP:
		/*
		 * Choose prctl+seccomp as the default mode if seccomp is
		 * enabled.
		 */
		if (IS_ENABLED(CONFIG_SECCOMP))
			mode = SPEC_STORE_BYPASS_SECCOMP;
		else
			mode = SPEC_STORE_BYPASS_PRCTL;
		break;
	case SPEC_STORE_BYPASS_CMD_ON:
		mode = SPEC_STORE_BYPASS_DISABLE;
		break;
	case SPEC_STORE_BYPASS_CMD_AUTO:
	case SPEC_STORE_BYPASS_CMD_PRCTL:
		mode = SPEC_STORE_BYPASS_PRCTL;
		break;
	case SPEC_STORE_BYPASS_CMD_NONE:
		break;
	}

	/*
	 * We have three CPU feature flags that are in play here:
	 *  - X86_BUG_SPEC_STORE_BYPASS - CPU is susceptible.
	 *  - X86_FEATURE_SSBD - CPU is able to turn off speculative store bypass
	 *  - X86_FEATURE_SPEC_STORE_BYPASS_DISABLE - engage the mitigation
	 */
	if (mode == SPEC_STORE_BYPASS_DISABLE) {
		setup_force_cpu_cap(X86_FEATURE_SPEC_STORE_BYPASS_DISABLE);
		/*
		 * Intel uses the SPEC CTRL MSR Bit(2) for this, while AMD may
		 * use a completely different MSR and bit dependent on family.
		 */
		if (!static_cpu_has(X86_FEATURE_SPEC_CTRL_SSBD) &&
		    !static_cpu_has(X86_FEATURE_AMD_SSBD)) {
			x86_amd_ssb_disable();
		} else {
			x86_spec_ctrl_base |= SPEC_CTRL_SSBD;
			update_spec_ctrl(x86_spec_ctrl_base);
		}
	}

	return mode;
}

It parses the kernel command line first via ssb_parse_cmdline()

x86_amd_ssb_disable()

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/kernel/cpu/bugs.c#L222

static void x86_amd_ssb_disable(void)
{
	u64 msrval = x86_amd_ls_cfg_base | x86_amd_ls_cfg_ssbd_mask;

	if (boot_cpu_has(X86_FEATURE_VIRT_SSBD))
		wrmsrl(MSR_AMD64_VIRT_SPEC_CTRL, SPEC_CTRL_SSBD);
	else if (boot_cpu_has(X86_FEATURE_LS_CFG_SSBD))
		wrmsrl(MSR_AMD64_LS_CFG, msrval);
}

Speculation Control via prctl()

Summary

  • Speculative store bypass can be disabled via prctl():
    • PR_SPEC_ENABLE (1): Enable speculation and disable mitigation.
    • PR_SPEC_DISABLE (2): Disable speculation and enable mitigation.
    • PR_SPEC_FORCE_DISABLE (3): Same as PR_SPEC_DISABLE but cannot be undone. A subsequent prctl(..., PR_SPEC_ENABLE) will fail.
    • PR_SPEC_DISABLE_NOEXEC (4): Same as PR_SPEC_DISABLE but the state will be cleared on execve().

prctl()

https://elixir.bootlin.com/linux/v6.11.5/source/kernel/sys.c#L2673

SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
		unsigned long, arg4, unsigned long, arg5)
{
// snipped
	switch (option) {
// snipped
	case PR_SET_SPECULATION_CTRL:
		if (arg4 || arg5)
			return -EINVAL;
		error = arch_prctl_spec_ctrl_set(me, arg2, arg3);
		break;
// snipped
	}
	return error;
}

arch_prctl_spec_ctrl_set()

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/kernel/cpu/bugs.c#L2285

int arch_prctl_spec_ctrl_set(struct task_struct *task, unsigned long which,
			     unsigned long ctrl)
{
	switch (which) {
	case PR_SPEC_STORE_BYPASS:
		return ssb_prctl_set(task, ctrl);
// snipped
	}
}

ssb_prctl_set()

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/kernel/cpu/bugs.c#L2177

static int ssb_prctl_set(struct task_struct *task, unsigned long ctrl)
{
	if (ssb_mode != SPEC_STORE_BYPASS_PRCTL &&
	    ssb_mode != SPEC_STORE_BYPASS_SECCOMP)
		return -ENXIO;

	switch (ctrl) {
	case PR_SPEC_ENABLE:
		/* If speculation is force disabled, enable is not allowed */
		if (task_spec_ssb_force_disable(task))
			return -EPERM;
		task_clear_spec_ssb_disable(task);
		task_clear_spec_ssb_noexec(task);
		task_update_spec_tif(task);
		break;
	case PR_SPEC_DISABLE:
		task_set_spec_ssb_disable(task);
		task_clear_spec_ssb_noexec(task);
		task_update_spec_tif(task);
		break;
	case PR_SPEC_FORCE_DISABLE:
		task_set_spec_ssb_disable(task);
		task_set_spec_ssb_force_disable(task);
		task_clear_spec_ssb_noexec(task);
		task_update_spec_tif(task);
		break;
	case PR_SPEC_DISABLE_NOEXEC:
		if (task_spec_ssb_force_disable(task))
			return -EPERM;
		task_set_spec_ssb_disable(task);
		task_set_spec_ssb_noexec(task);
		task_update_spec_tif(task);
		break;
	default:
		return -ERANGE;
	}
	return 0;
}

https://elixir.bootlin.com/linux/v6.11.5/source/include/uapi/linux/prctl.h#L217

/* Return and control values for PR_SET/GET_SPECULATION_CTRL */
# define PR_SPEC_NOT_AFFECTED		0
# define PR_SPEC_PRCTL			(1UL << 0)
# define PR_SPEC_ENABLE			(1UL << 1)
# define PR_SPEC_DISABLE		(1UL << 2)
# define PR_SPEC_FORCE_DISABLE		(1UL << 3)
# define PR_SPEC_DISABLE_NOEXEC		(1UL << 4)

https://docs.kernel.org/userspace-api/spec_ctrl.html

  • PR_SPEC_ENABLE (1): Enable speculation and disable mitigation.
  • PR_SPEC_DISABLE (2): Disable speculation and enable mitigation.
  • PR_SPEC_FORCE_DISABLE (3): Same as PR_SPEC_DISABLE but cannot be undone. A subsequent prctl(..., PR_SPEC_ENABLE) will fail.
  • PR_SPEC_DISABLE_NOEXEC (4): Same as PR_SPEC_DISABLE but the state will be cleared on execve().

task_{,set_,clear_}_{spec_ssb_disable,spec_ssb_noexec,spec_ssb_force_disable}()

https://elixir.bootlin.com/linux/v6.11.4/source/include/linux/sched.h#L1744

#define TASK_PFA_TEST(name, func)					\
	static inline bool task_##func(struct task_struct *p)		\
	{ return test_bit(PFA_##name, &p->atomic_flags); }

#define TASK_PFA_SET(name, func)					\
	static inline void task_set_##func(struct task_struct *p)	\
	{ set_bit(PFA_##name, &p->atomic_flags); }

#define TASK_PFA_CLEAR(name, func)					\
	static inline void task_clear_##func(struct task_struct *p)	\
	{ clear_bit(PFA_##name, &p->atomic_flags); }

// snipped
TASK_PFA_TEST(SPEC_SSB_DISABLE, spec_ssb_disable)
TASK_PFA_SET(SPEC_SSB_DISABLE, spec_ssb_disable)
TASK_PFA_CLEAR(SPEC_SSB_DISABLE, spec_ssb_disable)

TASK_PFA_TEST(SPEC_SSB_NOEXEC, spec_ssb_noexec)
TASK_PFA_SET(SPEC_SSB_NOEXEC, spec_ssb_noexec)
TASK_PFA_CLEAR(SPEC_SSB_NOEXEC, spec_ssb_noexec)

TASK_PFA_TEST(SPEC_SSB_FORCE_DISABLE, spec_ssb_force_disable)
TASK_PFA_SET(SPEC_SSB_FORCE_DISABLE, spec_ssb_force_disable)

task_update_spec_tif()

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/kernel/cpu/bugs.c#L2142

static void task_update_spec_tif(struct task_struct *tsk)
{
	/* Force the update of the real TIF bits */
	set_tsk_thread_flag(tsk, TIF_SPEC_FORCE_UPDATE);

	/*
	 * Immediately update the speculation control MSRs for the current
	 * task, but for a non-current task delay setting the CPU
	 * mitigation until it is scheduled next.
	 *
	 * This can only happen for SECCOMP mitigation. For PRCTL it's
	 * always the current task.
	 */
	if (tsk == current)
		speculation_ctrl_update_current();
}

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/include/asm/thread_info.h#L104

#define TIF_SPEC_FORCE_UPDATE	23	/* Force speculation MSR update in context switch */

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/kernel/process.c#L674

/* Called from seccomp/prctl update */
void speculation_ctrl_update_current(void)
{
	preempt_disable();
	speculation_ctrl_update(speculation_ctrl_update_tif(current));
	preempt_enable();
}

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/kernel/process.c#L646

static unsigned long speculation_ctrl_update_tif(struct task_struct *tsk)
{
	if (test_and_clear_tsk_thread_flag(tsk, TIF_SPEC_FORCE_UPDATE)) {
		if (task_spec_ssb_disable(tsk))
			set_tsk_thread_flag(tsk, TIF_SSBD);
		else
			clear_tsk_thread_flag(tsk, TIF_SSBD);

// snipped
	}
	/* Return the updated threadinfo flags*/
	return read_task_thread_flags(tsk);
}

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/kernel/process.c#L663

void speculation_ctrl_update(unsigned long tif)
{
	unsigned long flags;

	/* Forced update. Make sure all relevant TIF flags are different */
	local_irq_save(flags);
	__speculation_ctrl_update(~tif, tif);
	local_irq_restore(flags);
}

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/kernel/process.c#L613

/*
 * Update the MSRs managing speculation control, during context switch.
 *
 * tifp: Previous task's thread flags
 * tifn: Next task's thread flags
 */
static __always_inline void __speculation_ctrl_update(unsigned long tifp,
						      unsigned long tifn)
{
	unsigned long tif_diff = tifp ^ tifn;
	u64 msr = x86_spec_ctrl_base;
	bool updmsr = false;

	lockdep_assert_irqs_disabled();

	/* Handle change of TIF_SSBD depending on the mitigation method. */
	if (static_cpu_has(X86_FEATURE_VIRT_SSBD)) {
		if (tif_diff & _TIF_SSBD)
			amd_set_ssb_virt_state(tifn);
	} else if (static_cpu_has(X86_FEATURE_LS_CFG_SSBD)) {
		if (tif_diff & _TIF_SSBD)
			amd_set_core_ssb_state(tifn);
	} else if (static_cpu_has(X86_FEATURE_SPEC_CTRL_SSBD) ||
		   static_cpu_has(X86_FEATURE_AMD_SSBD)) {
		updmsr |= !!(tif_diff & _TIF_SSBD);
		msr |= ssbd_tif_to_spec_ctrl(tifn);
	}

	/* Only evaluate TIF_SPEC_IB if conditional STIBP is enabled. */
	if (IS_ENABLED(CONFIG_SMP) &&
	    static_branch_unlikely(&switch_to_cond_stibp)) {
		updmsr |= !!(tif_diff & _TIF_SPEC_IB);
		msr |= stibp_tif_to_spec_ctrl(tifn);
	}

	if (updmsr)
		update_spec_ctrl_cond(msr);
}

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/kernel/cpu/bugs.c#L81

/*
 * Keep track of the SPEC_CTRL MSR value for the current task, which may differ
 * from x86_spec_ctrl_base due to STIBP/SSB in __speculation_ctrl_update().
 */
void update_spec_ctrl_cond(u64 val)
{
	if (this_cpu_read(x86_spec_ctrl_current) == val)
		return;

	this_cpu_write(x86_spec_ctrl_current, val);

	/*
	 * When KERNEL_IBRS this MSR is written on return-to-user, unless
	 * forced the update can be delayed until that time.
	 */
	if (!cpu_feature_enabled(X86_FEATURE_KERNEL_IBRS))
		wrmsrl(MSR_IA32_SPEC_CTRL, val);
}

Speculation Control via seccomp

Summary

  • If spec_store_bypass_disable=seccomp, seccomp applies the same effect of prctl() with PR_SPEC_FORCE_DISABLE.

seccomp_assign_mode()

https://elixir.bootlin.com/linux/v6.11.5/source/kernel/seccomp.c#L449

static inline void seccomp_assign_mode(struct task_struct *task,
				       unsigned long seccomp_mode,
				       unsigned long flags)
{
// snipped
	/* Assume default seccomp processes want spec flaw mitigation. */
	if ((flags & SECCOMP_FILTER_FLAG_SPEC_ALLOW) == 0)
		arch_seccomp_spec_mitigate(task);
// snipped
}

https://elixir.bootlin.com/linux/v6.11.5/source/include/uapi/linux/seccomp.h#L23

/* Valid flags for SECCOMP_SET_MODE_FILTER */
// snipped
#define SECCOMP_FILTER_FLAG_SPEC_ALLOW		(1UL << 2)

arch_seccomp_spec_mitigate()

https://elixir.bootlin.com/linux/v6.11.5/source/arch/x86/kernel/cpu/bugs.c#L2296

#ifdef CONFIG_SECCOMP
void arch_seccomp_spec_mitigate(struct task_struct *task)
{
	if (ssb_mode == SPEC_STORE_BYPASS_SECCOMP)
		ssb_prctl_set(task, PR_SPEC_FORCE_DISABLE);
// snipped
}
#endif

History of Kernel Patches

Note: The lines marked * are not much related to SSB.

Date No. Patch Links
2018-03-21 - The final SSB bundle [lore] [commit]
2018-05-03 01 (*) x86/nospec: Simplify alternative_msr_write() [commit]
2018-05-03 02 (*) x86/bugs: Concentrate bug detection into a separate function [commit]
2018-05-03 03 (*) x86/bugs: Concentrate bug reporting into a separate function [commit]
2018-05-03 04 (*) x86/bugs: Read SPEC_CTRL MSR during boot and re-use reserved bits [commit]
2018-05-03 05 (*) x86/bugs, KVM: Support the combination of guest and host IBRS [commit]
2018-05-03 06 x86/bugs: Expose /sys/../spec_store_bypass [commit]
2018-05-03 07 x86/cpufeatures: Add X86_FEATURE_RDS [commit]
2018-05-03 08 x86/bugs: Provide boot parameters for the spec_store_bypass_disable mitigation [commit]
2018-05-03 09 x86/bugs/intel: Set proper CPU features and setup RDS [commit]
2018-05-03 10 x86/bugs: Whitelist allowed SPEC_CTRL MSR values [commit]
2018-05-03 11 x86/bugs/AMD: Add support to disable RDS on Fam[15,16,17]h if requested [commit]
2018-05-03 12 x86/KVM/VMX: Expose SPEC_CTRL Bit(2) to the guest [commit]
2018-05-03 13 x86/speculation: Create spec-ctrl.h to avoid include hell [commit]
2018-05-03 14 prctl: Add speculation control prctls [commit]
2018-05-03 15 x86/process: Allow runtime control of Speculative Store Bypass [commit]
2018-05-03 16 x86/speculation: Add prctl for Speculative Store Bypass mitigation [commit]
2018-05-03 17 nospec: Allow getting/setting on non-current task [commit]
2018-05-03 18 proc: Provide details on speculation flaw mitigations [commit]
2018-05-03 19 seccomp: Enable speculation flaw mitigations [commit]
2018-05-04 20 x86/bugs: Make boot modes __ro_after_init [commit]
2018-05-04 21 prctl: Add force disable speculation [commit]
2018-05-04 22 seccomp: Use PR_SPEC_FORCE_DISABLE [commit]
2018-05-04 23 seccomp: Add filter flag to opt-out of SSB mitigation [commit]
2018-05-04 24 seccomp: Move speculation migitation control to arch code [commit]
2018-05-04 25 x86/speculation: Make "seccomp" the default mode for Speculative Store Bypass [commit]
2018-05-09 26 x86/bugs: Rename _RDS to _SSBD [commit]
2018-05-09 27 proc: Use underscores for SSBD in status [commit]
2018-05-09 28 Documentation/spec_ctrl: Do some minor cleanups [commit]
2018-05-10 29 x86/bugs: Fix __ssb_select_mitigation() return type [commit]
2018-05-10 30 (*) x86/bugs: Make cpu_show_common() static [commit]
2018-05-12 31 (*) x86/bugs: Fix the parameters alignment and missing void [commit]
2018-05-13 32 (*) x86/cpu: Make alternative_msr_write work for 32-bit code [commit]
2018-05-17 33 (*) KVM: SVM: Move spec control call after restore of GS [commit]
2018-05-17 34 (*) x86/speculation: Use synthetic bits for IBRS/IBPB/STIBP [commit]
2018-05-17 35 (*) x86/cpufeatures: Disentangle MSR_SPEC_CTRL enumeration from IBRS [commit]
2018-05-17 36 x86/cpufeatures: Disentangle SSBD enumeration [commit]
2018-05-17 37 x86/cpufeatures: Add FEATURE_ZEN [commit]
2018-05-17 38 x86/speculation: Handle HT correctly on AMD [commit]
2018-05-17 39 x86/bugs, KVM: Extend speculation control for VIRT_SPEC_CTRL [commit]
2018-05-17 40 x86/speculation: Add virtualized speculative store bypass disable support [commit]
2018-05-17 41 x86/speculation: Rework speculative_store_bypass_update() [commit]
2018-05-17 42 x86/bugs: Unify x86_spec_ctrl_{set_guest,restore_host} [commit]
2018-05-17 43 x86/bugs: Expose x86_spec_ctrl_base directly [commit]
2018-05-17 44 x86/bugs: Remove x86_spec_ctrl_set() [commit]
2018-05-17 45 x86/bugs: Rework spec_ctrl base and mask logic [commit]
2018-05-17 46 x86/speculation, KVM: Implement support for VIRT_SPEC_CTRL/LS_CFG [commit]
2018-05-17 47 KVM: SVM: Implement VIRT_SPEC_CTRL support for SSBD [commit]
2018-05-18 48 x86/bugs: Rename SSBD_NO to SSB_NO [commit]
2018-05-19 49 bpf: Prevent memory disambiguation attack [commit]
2018-06-01 - AMD SSB bits. [lore]
2018-06-06 [1/3] x86/bugs: Add AMD's variant of SSB_NO [commit] [lore]
2018-06-06 [2/3] x86/bugs: Add AMD's SPEC_CTRL MSR usage [commit] [lore]
2018-06-06 [3/3] x86/bugs: Switch the selection of mitigation from CPU vendor to CPU features [commit] [lore]
2021-10-04 - x86: change default to spec_store_bypass_disable=prctl spectre_v2_user=prctl [commit] [lore]

References

GitHubで編集を提案

Discussion