Skip to content

Commit aa5cacd

Browse files
nivedita76suryasaimadhu
authored andcommitted
x86/asm: Replace __force_order with a memory clobber
The CRn accessor functions use __force_order as a dummy operand to prevent the compiler from reordering CRn reads/writes with respect to each other. The fact that the asm is volatile should be enough to prevent this: volatile asm statements should be executed in program order. However GCC 4.9.x and 5.x have a bug that might result in reordering. This was fixed in 8.1, 7.3 and 6.5. Versions prior to these, including 5.x and 4.9.x, may reorder volatile asm statements with respect to each other. There are some issues with __force_order as implemented: - It is used only as an input operand for the write functions, and hence doesn't do anything additional to prevent reordering writes. - It allows memory accesses to be cached/reordered across write functions, but CRn writes affect the semantics of memory accesses, so this could be dangerous. - __force_order is not actually defined in the kernel proper, but the LLVM toolchain can in some cases require a definition: LLVM (as well as GCC 4.9) requires it for PIE code, which is why the compressed kernel has a definition, but also the clang integrated assembler may consider the address of __force_order to be significant, resulting in a reference that requires a definition. Fix this by: - Using a memory clobber for the write functions to additionally prevent caching/reordering memory accesses across CRn writes. - Using a dummy input operand with an arbitrary constant address for the read functions, instead of a global variable. This will prevent reads from being reordered across writes, while allowing memory loads to be cached/reordered across CRn reads, which should be safe. Signed-off-by: Arvind Sankar <[email protected]> Signed-off-by: Borislav Petkov <[email protected]> Reviewed-by: Kees Cook <[email protected]> Reviewed-by: Miguel Ojeda <[email protected]> Tested-by: Nathan Chancellor <[email protected]> Tested-by: Sedat Dilek <[email protected]> Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82602 Link: https://lore.kernel.org/lkml/[email protected]/ Link: https://lkml.kernel.org/r/[email protected]
1 parent 767ec72 commit aa5cacd

File tree

3 files changed

+17
-24
lines changed

3 files changed

+17
-24
lines changed

arch/x86/boot/compressed/pgtable_64.c

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,6 @@
55
#include "pgtable.h"
66
#include "../string.h"
77

8-
/*
9-
* __force_order is used by special_insns.h asm code to force instruction
10-
* serialization.
11-
*
12-
* It is not referenced from the code, but GCC < 5 with -fPIE would fail
13-
* due to an undefined symbol. Define it to make these ancient GCCs work.
14-
*/
15-
unsigned long __force_order;
16-
178
#define BIOS_START_MIN 0x20000U /* 128K, less than this is insane */
189
#define BIOS_START_MAX 0x9f000U /* 640K, absolute maximum */
1910

arch/x86/include/asm/special_insns.h

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,45 +11,47 @@
1111
#include <linux/jump_label.h>
1212

1313
/*
14-
* Volatile isn't enough to prevent the compiler from reordering the
15-
* read/write functions for the control registers and messing everything up.
16-
* A memory clobber would solve the problem, but would prevent reordering of
17-
* all loads stores around it, which can hurt performance. Solution is to
18-
* use a variable and mimic reads and writes to it to enforce serialization
14+
* The compiler should not reorder volatile asm statements with respect to each
15+
* other: they should execute in program order. However GCC 4.9.x and 5.x have
16+
* a bug (which was fixed in 8.1, 7.3 and 6.5) where they might reorder
17+
* volatile asm. The write functions are not affected since they have memory
18+
* clobbers preventing reordering. To prevent reads from being reordered with
19+
* respect to writes, use a dummy memory operand.
1920
*/
20-
extern unsigned long __force_order;
21+
22+
#define __FORCE_ORDER "m"(*(unsigned int *)0x1000UL)
2123

2224
void native_write_cr0(unsigned long val);
2325

2426
static inline unsigned long native_read_cr0(void)
2527
{
2628
unsigned long val;
27-
asm volatile("mov %%cr0,%0\n\t" : "=r" (val), "=m" (__force_order));
29+
asm volatile("mov %%cr0,%0\n\t" : "=r" (val) : __FORCE_ORDER);
2830
return val;
2931
}
3032

3133
static __always_inline unsigned long native_read_cr2(void)
3234
{
3335
unsigned long val;
34-
asm volatile("mov %%cr2,%0\n\t" : "=r" (val), "=m" (__force_order));
36+
asm volatile("mov %%cr2,%0\n\t" : "=r" (val) : __FORCE_ORDER);
3537
return val;
3638
}
3739

3840
static __always_inline void native_write_cr2(unsigned long val)
3941
{
40-
asm volatile("mov %0,%%cr2": : "r" (val), "m" (__force_order));
42+
asm volatile("mov %0,%%cr2": : "r" (val) : "memory");
4143
}
4244

4345
static inline unsigned long __native_read_cr3(void)
4446
{
4547
unsigned long val;
46-
asm volatile("mov %%cr3,%0\n\t" : "=r" (val), "=m" (__force_order));
48+
asm volatile("mov %%cr3,%0\n\t" : "=r" (val) : __FORCE_ORDER);
4749
return val;
4850
}
4951

5052
static inline void native_write_cr3(unsigned long val)
5153
{
52-
asm volatile("mov %0,%%cr3": : "r" (val), "m" (__force_order));
54+
asm volatile("mov %0,%%cr3": : "r" (val) : "memory");
5355
}
5456

5557
static inline unsigned long native_read_cr4(void)
@@ -64,10 +66,10 @@ static inline unsigned long native_read_cr4(void)
6466
asm volatile("1: mov %%cr4, %0\n"
6567
"2:\n"
6668
_ASM_EXTABLE(1b, 2b)
67-
: "=r" (val), "=m" (__force_order) : "0" (0));
69+
: "=r" (val) : "0" (0), __FORCE_ORDER);
6870
#else
6971
/* CR4 always exists on x86_64. */
70-
asm volatile("mov %%cr4,%0\n\t" : "=r" (val), "=m" (__force_order));
72+
asm volatile("mov %%cr4,%0\n\t" : "=r" (val) : __FORCE_ORDER);
7173
#endif
7274
return val;
7375
}

arch/x86/kernel/cpu/common.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ void native_write_cr0(unsigned long val)
359359
unsigned long bits_missing = 0;
360360

361361
set_register:
362-
asm volatile("mov %0,%%cr0": "+r" (val), "+m" (__force_order));
362+
asm volatile("mov %0,%%cr0": "+r" (val) : : "memory");
363363

364364
if (static_branch_likely(&cr_pinning)) {
365365
if (unlikely((val & X86_CR0_WP) != X86_CR0_WP)) {
@@ -378,7 +378,7 @@ void native_write_cr4(unsigned long val)
378378
unsigned long bits_changed = 0;
379379

380380
set_register:
381-
asm volatile("mov %0,%%cr4": "+r" (val), "+m" (cr4_pinned_bits));
381+
asm volatile("mov %0,%%cr4": "+r" (val) : : "memory");
382382

383383
if (static_branch_likely(&cr_pinning)) {
384384
if (unlikely((val & cr4_pinned_mask) != cr4_pinned_bits)) {

0 commit comments

Comments
 (0)