Skip to content

Commit

Permalink
16461 Introduce sequence to clear Branch History Buffer (BHB)
Browse files Browse the repository at this point in the history
Reviewed by: Robert Mustacchi <rm@fingolfin.org>
Reviewed by: Andy Fiddaman <illumos@fiddaman.net>
Approved by: Gordon Ross <Gordon.W.Ross@gmail.com>
  • Loading branch information
danmcd committed Apr 10, 2024
1 parent c186a85 commit 5a9c36d
Show file tree
Hide file tree
Showing 4 changed files with 223 additions and 11 deletions.
54 changes: 54 additions & 0 deletions usr/src/uts/intel/ml/retpoline.S
Expand Up @@ -162,6 +162,60 @@ rsb_loop:
SET_SIZE(x86_rsb_stuff)
SET_SIZE(x86_rsb_stuff_vmexit)

/*
* The x86_bhb_clear() function is similar to x86_rsb_stuff(),
* including its reasons for conservative register preservation, but
* it clears branch-history with a software sequence from this
* document (pardon the long URL):
*/
/* BEGIN CSTYLED */
/*
* https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/technical-documentation/branch-history-injection.html
*/
/* END CSTYLED */
/*
* The patchable-spot is a NOP which can be patched with a RET if
* the CPU is properly working (either too old, mitigated, or actually
* fixed, see cpuid.c).
*/
ENTRY_NP(x86_bhb_clear)
nop
pushq %rcx
pushq %rax
pushq %rbx
movq %rsp, %rbx

/* INTEL-PROVIDED SEQUENCE START */
movl $5, %ecx
call 1f
jmp 5f
.align 64
1:
call 2f
ret
.align 64
2:
movl $5, %eax
3:
jmp 4f
nop
4:
sub $1, %eax
jnz 3b
sub $1, %ecx
jnz 1b
ret
5:
lfence
/* INTEL-PROVIDED SEQUENCE FINISH */

movq %rbx, %rsp
popq %rbx
popq %rax
popq %rcx
ret
SET_SIZE(x86_bhb_clear)

#elif defined(__i386)

/*
Expand Down
13 changes: 12 additions & 1 deletion usr/src/uts/intel/ml/swtch.S
Expand Up @@ -25,6 +25,7 @@

/*
* Copyright 2020 Joyent, Inc.
* Copyright 2024 MNX Cloud, Inc.
*/

/*
Expand Down Expand Up @@ -173,11 +174,21 @@
/*
* Take a moment to potentially clear the RSB buffer. This is done to
* prevent various Spectre variant 2 and SpectreRSB attacks. This may
* not be sufficient. Please see uts/intel/ml/retpoline.s for more
* not be sufficient. Please see uts/intel/ml/retpoline.S for more
* information about this.
*/
call x86_rsb_stuff

/*
* Take another moment to potentially clear the branch history buffer
* (BHB). This is done to prevent recent discoveries that branch
* history can also be trained to exploit certain compiler-generated
* instruction sequences (known as "gadgets") to leak data
* speculatively. As with x86_rsb_stuff, see retpoline.S, and this
* may not be sufficient.
*/
call x86_bhb_clear

/*
* Save non-volatile registers, and set return address for current
* thread to resume_return.
Expand Down
158 changes: 150 additions & 8 deletions usr/src/uts/intel/os/cpuid.c
Expand Up @@ -1107,6 +1107,7 @@
* - Spectre v1
* - swapgs (Spectre v1 variant)
* - Spectre v2
* - Branch History Injection (BHI).
* - Meltdown (Spectre v3)
* - Rogue Register Read (Spectre v3a)
* - Speculative Store Bypass (Spectre v4)
Expand Down Expand Up @@ -1251,6 +1252,33 @@
* it may make more sense to investigate using prediction barriers as the whole
* system is only executing a single instruction at a time while in kmdb.
*
* Branch History Injection (BHI)
*
* BHI is a specific form of SPECTREv2 where an attacker may manipulate branch
* history before transitioning from user to supervisor mode (or from VMX
* non-root/guest to root mode). The attacker can then exploit certain
* compiler-generated code-sequences ("gadgets") to disclose information from
* other contexts or domains. Recent (late-2023/early-2024) research in
* object code analysis discovered many more potential gadgets than what was
* initially reported (which previously was confined to Linux use of
* unprivileged eBPF).
*
* The BHI threat doesn't exist in processsors that predate eIBRS, or in AMD
* ones. Some eIBRS processors have the ability to disable branch history in
* certain (but not all) cases using an MSR write. eIBRS processors that don't
* have the ability to disable must use a software sequence to scrub the
* branch history buffer.
*
* BHI_DIS_S (the aforementioned MSR) prevents ring 0 from ring 3 (VMX guest
* or VMX root). It does not protect different user processes from each other,
* or ring 3 VMX guest from ring 3 VMX root or vice versa.
*
* The BHI clearing sequence prevents user exploiting kernel gadgets, and user
* A's use of user B's gadgets.
*
* SMEP and eIBRS are a continuing defense-in-depth measure protecting the
* kernel.
*
* SPECTRE v1, v4
*
* The v1 and v4 variants of spectre are not currently mitigated in the
Expand Down Expand Up @@ -1478,6 +1506,7 @@
* - MDS: x86_md_clear, requires microcode, disabling SMT
* - TAA: x86_md_clear and disabling SMT OR microcode and disabling TSX
* - RFDS: microcode with x86_md_clear if RFDS_CLEAR set and RFDS_NO not.
* - BHI: software sequence, and use of BHI_DIS_S if microcode has it.
*
* The following table indicates the x86 feature set bits that indicate that a
* given problem has been solved or a notable feature is present:
Expand All @@ -1486,6 +1515,7 @@
* - MDS_NO: All forms of MDS
* - TAA_NO: TAA
* - RFDS_NO: RFDS
* - BHI_NO: BHI
*/

#include <sys/types.h>
Expand Down Expand Up @@ -1680,7 +1710,9 @@ static char *x86_feature_names[NUM_X86_FEATURES] = {
"auto_ibrs",
"rfds_no",
"rfds_clear",
"pbrsb_no"
"pbrsb_no",
"bhi_no",
"bhi_clear"
};

boolean_t
Expand Down Expand Up @@ -1833,7 +1865,7 @@ struct cpuid_info {
/* Intel fn: 4, AMD fn: 8000001d */
struct cpuid_regs **cpi_cache_leaves; /* Actual leaves from above */
struct cpuid_regs cpi_std[NMAX_CPI_STD]; /* 0 .. 7 */
struct cpuid_regs cpi_sub7[1]; /* Leaf 7, sub-leaf 1 */
struct cpuid_regs cpi_sub7[2]; /* Leaf 7, sub-leaves 1-2 */
/*
* extended function information
*/
Expand Down Expand Up @@ -1913,6 +1945,7 @@ static struct cpuid_info cpuid_info0;
#define CPI_FEATURES_7_0_ECX(cpi) ((cpi)->cpi_std[7].cp_ecx)
#define CPI_FEATURES_7_0_EDX(cpi) ((cpi)->cpi_std[7].cp_edx)
#define CPI_FEATURES_7_1_EAX(cpi) ((cpi)->cpi_sub7[0].cp_eax)
#define CPI_FEATURES_7_2_EDX(cpi) ((cpi)->cpi_sub7[1].cp_edx)

#define CPI_BRANDID(cpi) BITX((cpi)->cpi_std[1].cp_ebx, 7, 0)
#define CPI_CHUNKS(cpi) BITX((cpi)->cpi_std[1].cp_ebx, 15, 7)
Expand Down Expand Up @@ -2972,6 +3005,88 @@ cpuid_update_l1d_flush(cpu_t *cpu, uchar_t *featureset)
membar_producer();
}

/*
* Branch History Injection (BHI) mitigations.
*
* Intel has provided a software sequence that will scrub the BHB. Like RSB
* (below) we can scribble a return at the beginning to avoid if if the CPU
* is modern enough. We can also scribble a return if the CPU is old enough
* to not have an RSB (pre-eIBRS).
*/
typedef enum {
X86_BHI_TOO_OLD_OR_DISABLED, /* Pre-eIBRS or disabled */
X86_BHI_NEW_ENOUGH, /* AMD, or Intel with BHI_NO set */
X86_BHI_DIS_S, /* BHI_NO == 0, but BHI_DIS_S avail. */
/* NOTE: BHI_DIS_S above will still need the software sequence. */
X86_BHI_SOFTWARE_SEQUENCE, /* Use software sequence */
} x86_native_bhi_mitigation_t;

x86_native_bhi_mitigation_t x86_bhi_mitigation = X86_BHI_SOFTWARE_SEQUENCE;

static void
cpuid_enable_bhi_dis_s(void)
{
uint64_t val;

val = rdmsr(MSR_IA32_SPEC_CTRL);
val |= IA32_SPEC_CTRL_BHI_DIS_S;
wrmsr(MSR_IA32_SPEC_CTRL, val);
}

/*
* This function scribbles RET into the first instruction of x86_bhb_clear()
* if SPECTREV2 mitigations are disabled, the CPU is too old, the CPU is new
* enough to fix (which includes non-Intel CPUs), or the CPU has an explicit
* disable-Branch-History control.
*/
static x86_native_bhi_mitigation_t
cpuid_learn_and_patch_bhi(x86_spectrev2_mitigation_t v2mit, cpu_t *cpu,
uchar_t *featureset)
{
struct cpuid_info *cpi = cpu->cpu_m.mcpu_cpi;
const uint8_t ret = RET_INSTR;
uint8_t *bhb_clear = (uint8_t *)x86_bhb_clear;

ASSERT0(cpu->cpu_id);

/* First check for explicitly disabled... */
if (v2mit == X86_SPECTREV2_DISABLED) {
*bhb_clear = ret;
return (X86_BHI_TOO_OLD_OR_DISABLED);
}

/*
* Then check for BHI_NO, which means the CPU doesn't have this bug,
* or if it's non-Intel, in which case this mitigation mechanism
* doesn't apply.
*/
if (cpi->cpi_vendor != X86_VENDOR_Intel ||
is_x86_feature(featureset, X86FSET_BHI_NO)) {
*bhb_clear = ret;
return (X86_BHI_NEW_ENOUGH);
}

/*
* Now check for the BHI_CTRL MSR, and then set it if available.
* We will still need to use the software sequence, however.
*/
if (is_x86_feature(featureset, X86FSET_BHI_CTRL)) {
cpuid_enable_bhi_dis_s();
return (X86_BHI_DIS_S);
}

/*
* Finally, check if we are too old to bother with RSB:
*/
if (v2mit == X86_SPECTREV2_RETPOLINE) {
*bhb_clear = ret;
return (X86_BHI_TOO_OLD_OR_DISABLED);
}

ASSERT(*bhb_clear != ret);
return (X86_BHI_SOFTWARE_SEQUENCE);
}

/*
* We default to enabling Return Stack Buffer (RSB) mitigations.
*
Expand Down Expand Up @@ -3269,6 +3384,14 @@ cpuid_scan_security(cpu_t *cpu, uchar_t *featureset)
add_x86_feature(featureset, X86FSET_STIBP);
}

/*
* Some prediction controls are enumerated by subleaf 2 of
* leaf 7.
*/
if (CPI_FEATURES_7_2_EDX(cpi) & CPUID_INTC_EDX_7_2_BHI_CTRL) {
add_x86_feature(featureset, X86FSET_BHI_CTRL);
}

/*
* Don't read the arch caps MSR on xpv where we lack the
* on_trap().
Expand Down Expand Up @@ -3328,6 +3451,10 @@ cpuid_scan_security(cpu_t *cpu, uchar_t *featureset)
add_x86_feature(featureset,
X86FSET_PBRSB_NO);
}
if (reg & IA32_ARCH_CAP_BHI_NO) {
add_x86_feature(featureset,
X86FSET_BHI_NO);
}
}
no_trap();
}
Expand Down Expand Up @@ -3358,6 +3485,10 @@ cpuid_scan_security(cpu_t *cpu, uchar_t *featureset)
break;
}

/* If we're committed to BHI_DIS_S, set it for this core. */
if (x86_bhi_mitigation == X86_BHI_DIS_S)
cpuid_enable_bhi_dis_s();

cpuid_apply_tsx(x86_taa_mitigation, featureset);
return;
}
Expand All @@ -3370,10 +3501,11 @@ cpuid_scan_security(cpu_t *cpu, uchar_t *featureset)

/*
* By default we've come in with retpolines enabled. Check whether we
* should disable them or enable enhanced or automatic IBRS. RSB
* stuffing is enabled by default. Note, we do not allow the use of AMD
* optimized retpolines as it was disclosed by AMD in March 2022 that
* they were still vulnerable. Prior to that point, we used them.
* should disable them or enable enhanced or automatic IBRS.
*
* Note, we do not allow the use of AMD optimized retpolines as it was
* disclosed by AMD in March 2022 that they were still
* vulnerable. Prior to that point, we used them.
*/
if (x86_disable_spectrev2 != 0) {
v2mit = X86_SPECTREV2_DISABLED;
Expand All @@ -3389,6 +3521,7 @@ cpuid_scan_security(cpu_t *cpu, uchar_t *featureset)

cpuid_patch_retpolines(v2mit);
cpuid_patch_rsb(v2mit, is_x86_feature(featureset, X86FSET_PBRSB_NO));
x86_bhi_mitigation = cpuid_learn_and_patch_bhi(v2mit, cpu, featureset);
x86_spectrev2_mitigation = v2mit;
membar_producer();

Expand Down Expand Up @@ -4174,8 +4307,8 @@ cpuid_pass_basic(cpu_t *cpu, void *arg)
}

/*
* If we have subleaf 1 available, grab and store that. This is
* used for more AVX and related features.
* If we have subleaf 1 or 2 available, grab and store
* that. This is used for more AVX and related features.
*/
if (ecp->cp_eax >= 1) {
struct cpuid_regs *c71;
Expand All @@ -4184,6 +4317,15 @@ cpuid_pass_basic(cpu_t *cpu, void *arg)
c71->cp_ecx = 1;
(void) __cpuid_insn(c71);
}

/* Subleaf 2 has certain security indicators in it. */
if (ecp->cp_eax >= 2) {
struct cpuid_regs *c72;
c72 = &cpi->cpi_sub7[1];
c72->cp_eax = 7;
c72->cp_ecx = 2;
(void) __cpuid_insn(c72);
}
}

/*
Expand Down
9 changes: 7 additions & 2 deletions usr/src/uts/intel/sys/x86_archext.h
Expand Up @@ -507,6 +507,8 @@ extern "C" {
#define CPUID_INTC_EAX_7_1_LAM 0x02000000 /* Linear addr mask */
/* bits 27-31 are reserved */

#define CPUID_INTC_EDX_7_2_BHI_CTRL (1U << 4U) /* BHI controls */

/*
* Intel also uses cpuid leaf 0xd to report additional instructions and features
* when the sub-leaf in %ecx == 1. We label these using the same convention as
Expand Down Expand Up @@ -935,6 +937,8 @@ extern "C" {
#define X86FSET_RFDS_NO 109
#define X86FSET_RFDS_CLEAR 110
#define X86FSET_PBRSB_NO 111
#define X86FSET_BHI_NO 112
#define X86FSET_BHI_CTRL 113

/*
* Intel Deep C-State invariant TSC in leaf 0x80000007.
Expand Down Expand Up @@ -1598,7 +1602,7 @@ typedef enum x86_uarchrev {

#if defined(_KERNEL) || defined(_KMEMUSER)

#define NUM_X86_FEATURES 112
#define NUM_X86_FEATURES 114
extern uchar_t x86_featureset[];

extern void free_x86_featureset(void *featureset);
Expand All @@ -1617,11 +1621,12 @@ extern uint_t pentiumpro_bug4046376;

/*
* These functions are all used to perform various side-channel mitigations.
* Please see uts/i86pc/os/cpuid.c for more information.
* Please see uts/intel/os/cpuid.c for more information.
*/
extern void (*spec_uarch_flush)(void);
extern void x86_rsb_stuff(void);
extern void x86_rsb_stuff_vmexit(void);
extern void x86_bhb_clear(void);
extern void x86_md_clear(void);

#endif
Expand Down

0 comments on commit 5a9c36d

Please sign in to comment.