Skip to content

Commit f796437

Browse files
npigginmpe
authored andcommitted
powerpc/64s: flush L1D on kernel entry
IBM Power9 processors can speculatively operate on data in the L1 cache before it has been completely validated, via a way-prediction mechanism. It is not possible for an attacker to determine the contents of impermissible memory using this method, since these systems implement a combination of hardware and software security measures to prevent scenarios where protected data could be leaked. However these measures don't address the scenario where an attacker induces the operating system to speculatively execute instructions using data that the attacker controls. This can be used for example to speculatively bypass "kernel user access prevention" techniques, as discovered by Anthony Steinhauser of Google's Safeside Project. This is not an attack by itself, but there is a possibility it could be used in conjunction with side-channels or other weaknesses in the privileged code to construct an attack. This issue can be mitigated by flushing the L1 cache between privilege boundaries of concern. This patch flushes the L1 cache on kernel entry. This is part of the fix for CVE-2020-4788. Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Daniel Axtens <dja@axtens.net> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
1 parent fcb4845 commit f796437

11 files changed

Lines changed: 200 additions & 2 deletions

File tree

Documentation/admin-guide/kernel-parameters.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2858,6 +2858,7 @@
28582858
mds=off [X86]
28592859
tsx_async_abort=off [X86]
28602860
kvm.nx_huge_pages=off [X86]
2861+
no_entry_flush [PPC]
28612862

28622863
Exceptions:
28632864
This does not have any effect on
@@ -3186,6 +3187,8 @@
31863187

31873188
noefi Disable EFI runtime services support.
31883189

3190+
no_entry_flush [PPC] Don't flush the L1-D cache when entering the kernel.
3191+
31893192
noexec [IA-64]
31903193

31913194
noexec [X86]

arch/powerpc/include/asm/exception-64s.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,18 @@
5757
nop; \
5858
nop
5959

60+
#define ENTRY_FLUSH_SLOT \
61+
ENTRY_FLUSH_FIXUP_SECTION; \
62+
nop; \
63+
nop; \
64+
nop;
65+
6066
/*
6167
* r10 must be free to use, r13 must be paca
6268
*/
6369
#define INTERRUPT_TO_KERNEL \
64-
STF_ENTRY_BARRIER_SLOT
70+
STF_ENTRY_BARRIER_SLOT; \
71+
ENTRY_FLUSH_SLOT
6572

6673
/*
6774
* Macros for annotating the expected destination of (h)rfid

arch/powerpc/include/asm/feature-fixups.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,14 @@ label##3: \
205205
FTR_ENTRY_OFFSET 955b-956b; \
206206
.popsection;
207207

208+
#define ENTRY_FLUSH_FIXUP_SECTION \
209+
957: \
210+
.pushsection __entry_flush_fixup,"a"; \
211+
.align 2; \
212+
958: \
213+
FTR_ENTRY_OFFSET 957b-958b; \
214+
.popsection;
215+
208216
#define RFI_FLUSH_FIXUP_SECTION \
209217
951: \
210218
.pushsection __rfi_flush_fixup,"a"; \
@@ -237,8 +245,10 @@ label##3: \
237245
#include <linux/types.h>
238246

239247
extern long stf_barrier_fallback;
248+
extern long entry_flush_fallback;
240249
extern long __start___stf_entry_barrier_fixup, __stop___stf_entry_barrier_fixup;
241250
extern long __start___stf_exit_barrier_fixup, __stop___stf_exit_barrier_fixup;
251+
extern long __start___entry_flush_fixup, __stop___entry_flush_fixup;
242252
extern long __start___rfi_flush_fixup, __stop___rfi_flush_fixup;
243253
extern long __start___barrier_nospec_fixup, __stop___barrier_nospec_fixup;
244254
extern long __start__btb_flush_fixup, __stop__btb_flush_fixup;

arch/powerpc/include/asm/security_features.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,16 @@ static inline bool security_ftr_enabled(u64 feature)
8686
// Software required to flush link stack on context switch
8787
#define SEC_FTR_FLUSH_LINK_STACK 0x0000000000001000ull
8888

89+
// The L1-D cache should be flushed when entering the kernel
90+
#define SEC_FTR_L1D_FLUSH_ENTRY 0x0000000000004000ull
91+
8992

9093
// Features enabled by default
9194
#define SEC_FTR_DEFAULT \
9295
(SEC_FTR_L1D_FLUSH_HV | \
9396
SEC_FTR_L1D_FLUSH_PR | \
9497
SEC_FTR_BNDS_CHK_SPEC_BAR | \
98+
SEC_FTR_L1D_FLUSH_ENTRY | \
9599
SEC_FTR_FAVOUR_SECURITY)
96100

97101
#endif /* _ASM_POWERPC_SECURITY_FEATURES_H */

arch/powerpc/include/asm/setup.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,15 @@ enum l1d_flush_type {
5252
};
5353

5454
void setup_rfi_flush(enum l1d_flush_type, bool enable);
55+
void setup_entry_flush(bool enable);
56+
void setup_uaccess_flush(bool enable);
5557
void do_rfi_flush_fixups(enum l1d_flush_type types);
5658
#ifdef CONFIG_PPC_BARRIER_NOSPEC
5759
void setup_barrier_nospec(void);
5860
#else
5961
static inline void setup_barrier_nospec(void) { };
6062
#endif
63+
void do_entry_flush_fixups(enum l1d_flush_type types);
6164
void do_barrier_nospec_fixups(bool enable);
6265
extern bool barrier_nospec_enabled;
6366

arch/powerpc/kernel/exceptions-64s.S

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2951,6 +2951,43 @@ TRAMP_REAL_BEGIN(stf_barrier_fallback)
29512951
.endr
29522952
blr
29532953

2954+
TRAMP_REAL_BEGIN(entry_flush_fallback)
2955+
std r9,PACA_EXRFI+EX_R9(r13)
2956+
std r10,PACA_EXRFI+EX_R10(r13)
2957+
std r11,PACA_EXRFI+EX_R11(r13)
2958+
mfctr r9
2959+
ld r10,PACA_RFI_FLUSH_FALLBACK_AREA(r13)
2960+
ld r11,PACA_L1D_FLUSH_SIZE(r13)
2961+
srdi r11,r11,(7 + 3) /* 128 byte lines, unrolled 8x */
2962+
mtctr r11
2963+
DCBT_BOOK3S_STOP_ALL_STREAM_IDS(r11) /* Stop prefetch streams */
2964+
2965+
/* order ld/st prior to dcbt stop all streams with flushing */
2966+
sync
2967+
2968+
/*
2969+
* The load addresses are at staggered offsets within cachelines,
2970+
* which suits some pipelines better (on others it should not
2971+
* hurt).
2972+
*/
2973+
1:
2974+
ld r11,(0x80 + 8)*0(r10)
2975+
ld r11,(0x80 + 8)*1(r10)
2976+
ld r11,(0x80 + 8)*2(r10)
2977+
ld r11,(0x80 + 8)*3(r10)
2978+
ld r11,(0x80 + 8)*4(r10)
2979+
ld r11,(0x80 + 8)*5(r10)
2980+
ld r11,(0x80 + 8)*6(r10)
2981+
ld r11,(0x80 + 8)*7(r10)
2982+
addi r10,r10,0x80*8
2983+
bdnz 1b
2984+
2985+
mtctr r9
2986+
ld r9,PACA_EXRFI+EX_R9(r13)
2987+
ld r10,PACA_EXRFI+EX_R10(r13)
2988+
ld r11,PACA_EXRFI+EX_R11(r13)
2989+
blr
2990+
29542991
TRAMP_REAL_BEGIN(rfi_flush_fallback)
29552992
SET_SCRATCH0(r13);
29562993
GET_PACA(r13);

arch/powerpc/kernel/setup_64.c

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -945,7 +945,9 @@ early_initcall(disable_hardlockup_detector);
945945
static enum l1d_flush_type enabled_flush_types;
946946
static void *l1d_flush_fallback_area;
947947
static bool no_rfi_flush;
948+
static bool no_entry_flush;
948949
bool rfi_flush;
950+
bool entry_flush;
949951

950952
static int __init handle_no_rfi_flush(char *p)
951953
{
@@ -955,6 +957,14 @@ static int __init handle_no_rfi_flush(char *p)
955957
}
956958
early_param("no_rfi_flush", handle_no_rfi_flush);
957959

960+
static int __init handle_no_entry_flush(char *p)
961+
{
962+
pr_info("entry-flush: disabled on command line.");
963+
no_entry_flush = true;
964+
return 0;
965+
}
966+
early_param("no_entry_flush", handle_no_entry_flush);
967+
958968
/*
959969
* The RFI flush is not KPTI, but because users will see doco that says to use
960970
* nopti we hijack that option here to also disable the RFI flush.
@@ -986,6 +996,18 @@ void rfi_flush_enable(bool enable)
986996
rfi_flush = enable;
987997
}
988998

999+
void entry_flush_enable(bool enable)
1000+
{
1001+
if (enable) {
1002+
do_entry_flush_fixups(enabled_flush_types);
1003+
on_each_cpu(do_nothing, NULL, 1);
1004+
} else {
1005+
do_entry_flush_fixups(L1D_FLUSH_NONE);
1006+
}
1007+
1008+
entry_flush = enable;
1009+
}
1010+
9891011
static void __ref init_fallback_flush(void)
9901012
{
9911013
u64 l1d_size, limit;
@@ -1044,10 +1066,19 @@ void setup_rfi_flush(enum l1d_flush_type types, bool enable)
10441066

10451067
enabled_flush_types = types;
10461068

1047-
if (!no_rfi_flush && !cpu_mitigations_off())
1069+
if (!cpu_mitigations_off() && !no_rfi_flush)
10481070
rfi_flush_enable(enable);
10491071
}
10501072

1073+
void setup_entry_flush(bool enable)
1074+
{
1075+
if (cpu_mitigations_off())
1076+
return;
1077+
1078+
if (!no_entry_flush)
1079+
entry_flush_enable(enable);
1080+
}
1081+
10511082
#ifdef CONFIG_DEBUG_FS
10521083
static int rfi_flush_set(void *data, u64 val)
10531084
{
@@ -1075,9 +1106,36 @@ static int rfi_flush_get(void *data, u64 *val)
10751106

10761107
DEFINE_SIMPLE_ATTRIBUTE(fops_rfi_flush, rfi_flush_get, rfi_flush_set, "%llu\n");
10771108

1109+
static int entry_flush_set(void *data, u64 val)
1110+
{
1111+
bool enable;
1112+
1113+
if (val == 1)
1114+
enable = true;
1115+
else if (val == 0)
1116+
enable = false;
1117+
else
1118+
return -EINVAL;
1119+
1120+
/* Only do anything if we're changing state */
1121+
if (enable != entry_flush)
1122+
entry_flush_enable(enable);
1123+
1124+
return 0;
1125+
}
1126+
1127+
static int entry_flush_get(void *data, u64 *val)
1128+
{
1129+
*val = entry_flush ? 1 : 0;
1130+
return 0;
1131+
}
1132+
1133+
DEFINE_SIMPLE_ATTRIBUTE(fops_entry_flush, entry_flush_get, entry_flush_set, "%llu\n");
1134+
10781135
static __init int rfi_flush_debugfs_init(void)
10791136
{
10801137
debugfs_create_file("rfi_flush", 0600, powerpc_debugfs_root, NULL, &fops_rfi_flush);
1138+
debugfs_create_file("entry_flush", 0600, powerpc_debugfs_root, NULL, &fops_entry_flush);
10811139
return 0;
10821140
}
10831141
device_initcall(rfi_flush_debugfs_init);

arch/powerpc/kernel/vmlinux.lds.S

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,13 @@ SECTIONS
131131
__stop___stf_entry_barrier_fixup = .;
132132
}
133133

134+
. = ALIGN(8);
135+
__entry_flush_fixup : AT(ADDR(__entry_flush_fixup) - LOAD_OFFSET) {
136+
__start___entry_flush_fixup = .;
137+
*(__entry_flush_fixup)
138+
__stop___entry_flush_fixup = .;
139+
}
140+
134141
. = ALIGN(8);
135142
__stf_exit_barrier_fixup : AT(ADDR(__stf_exit_barrier_fixup) - LOAD_OFFSET) {
136143
__start___stf_exit_barrier_fixup = .;

arch/powerpc/lib/feature-fixups.c

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,60 @@ void do_stf_barrier_fixups(enum stf_barrier_type types)
234234
do_stf_exit_barrier_fixups(types);
235235
}
236236

237+
void do_entry_flush_fixups(enum l1d_flush_type types)
238+
{
239+
unsigned int instrs[3], *dest;
240+
long *start, *end;
241+
int i;
242+
243+
start = PTRRELOC(&__start___entry_flush_fixup);
244+
end = PTRRELOC(&__stop___entry_flush_fixup);
245+
246+
instrs[0] = 0x60000000; /* nop */
247+
instrs[1] = 0x60000000; /* nop */
248+
instrs[2] = 0x60000000; /* nop */
249+
250+
i = 0;
251+
if (types == L1D_FLUSH_FALLBACK) {
252+
instrs[i++] = 0x7d4802a6; /* mflr r10 */
253+
instrs[i++] = 0x60000000; /* branch patched below */
254+
instrs[i++] = 0x7d4803a6; /* mtlr r10 */
255+
}
256+
257+
if (types & L1D_FLUSH_ORI) {
258+
instrs[i++] = 0x63ff0000; /* ori 31,31,0 speculation barrier */
259+
instrs[i++] = 0x63de0000; /* ori 30,30,0 L1d flush*/
260+
}
261+
262+
if (types & L1D_FLUSH_MTTRIG)
263+
instrs[i++] = 0x7c12dba6; /* mtspr TRIG2,r0 (SPR #882) */
264+
265+
for (i = 0; start < end; start++, i++) {
266+
dest = (void *)start + *start;
267+
268+
pr_devel("patching dest %lx\n", (unsigned long)dest);
269+
270+
patch_instruction((struct ppc_inst *)dest, ppc_inst(instrs[0]));
271+
272+
if (types == L1D_FLUSH_FALLBACK)
273+
patch_branch((struct ppc_inst *)(dest + 1), (unsigned long)&entry_flush_fallback,
274+
BRANCH_SET_LINK);
275+
else
276+
patch_instruction((struct ppc_inst *)(dest + 1), ppc_inst(instrs[1]));
277+
278+
patch_instruction((struct ppc_inst *)(dest + 2), ppc_inst(instrs[2]));
279+
}
280+
281+
printk(KERN_DEBUG "entry-flush: patched %d locations (%s flush)\n", i,
282+
(types == L1D_FLUSH_NONE) ? "no" :
283+
(types == L1D_FLUSH_FALLBACK) ? "fallback displacement" :
284+
(types & L1D_FLUSH_ORI) ? (types & L1D_FLUSH_MTTRIG)
285+
? "ori+mttrig type"
286+
: "ori type" :
287+
(types & L1D_FLUSH_MTTRIG) ? "mttrig type"
288+
: "unknown");
289+
}
290+
237291
void do_rfi_flush_fixups(enum l1d_flush_type types)
238292
{
239293
unsigned int instrs[3], *dest;

arch/powerpc/platforms/powernv/setup.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,23 @@ static void pnv_setup_rfi_flush(void)
122122
type = L1D_FLUSH_ORI;
123123
}
124124

125+
/*
126+
* If we are non-Power9 bare metal, we don't need to flush on kernel
127+
* entry: it fixes a P9 specific vulnerability.
128+
*/
129+
if (!pvr_version_is(PVR_POWER9))
130+
security_ftr_clear(SEC_FTR_L1D_FLUSH_ENTRY);
131+
125132
enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) && \
126133
(security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR) || \
127134
security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV));
128135

129136
setup_rfi_flush(type, enable);
130137
setup_count_cache_flush();
138+
139+
enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) &&
140+
security_ftr_enabled(SEC_FTR_L1D_FLUSH_ENTRY);
141+
setup_entry_flush(enable);
131142
}
132143

133144
static void __init pnv_check_guarded_cores(void)

0 commit comments

Comments
 (0)