Indirect branch tracking is a hardware security feature that verifies near
indirect call/jump instructions arrive at intended targets, which are
labeled by the compiler with ENDBR opcodes. If such instructions reach
unlabeled locations, the processor raises control-protection faults.
Check the compiler is up-to-date at config time.
Signed-off-by: Yu-cheng Yu <redacted>
Reviewed-by: Kees Cook <redacted>
---
arch/x86/Kconfig | 1 +
1 file changed, 1 insertion(+)
An ELF file's .note.gnu.property indicates features the file supports.
The property is parsed at loading time and passed to arch_setup_elf_
property(). Update it for Indirect Branch Tracking.
Signed-off-by: Yu-cheng Yu <redacted>
Reviewed-by: Kees Cook <redacted>
---
arch/x86/kernel/process_64.c | 8 ++++++++
1 file changed, 8 insertions(+)
When an indirect CALL/JMP instruction is executed and before it reaches
the target, it is in 'WAIT_ENDBR' status, which can be read from
MSR_IA32_U_CET. The status is part of a task's status before a signal is
raised and preserved in the signal frame. It is restored for sigreturn.
IBT state machine is described in Intel SDM Vol. 1, Sec. 18.3.
Signed-off-by: Yu-cheng Yu <redacted>
Reviewed-by: Kees Cook <redacted>
---
arch/x86/kernel/cet.c | 26 ++++++++++++++++++++++++--
arch/x86/kernel/fpu/signal.c | 8 +++++---
2 files changed, 29 insertions(+), 5 deletions(-)
@@ -577,7 +579,7 @@ static unsigned long fpu__alloc_sigcontext_ext(unsigned long sp)*sigcontext_extisat:fpu+fpu_user_xstate_size+*FP_XSTATE_MAGIC2_SIZE,thenalignedto8.*/-if(cet->shstk_size)+if(cet->shstk_size||cet->ibt_enabled)sp-=(sizeof(structsc_ext)+8);returnsp;
ENDBR is a special new instruction for the Indirect Branch Tracking (IBT)
component of CET. IBT prevents attacks by ensuring that (most) indirect
branches and function calls may only land at ENDBR instructions. Branches
that don't follow the rules will result in control flow (#CF) exceptions.
ENDBR is a noop when IBT is unsupported or disabled. Most ENDBR
instructions are inserted automatically by the compiler, but branch
targets written in assembly must have ENDBR added manually.
There are two ENDBR versions: one for 64-bit and the other for 32.
Introduce a macro to eliminate ifdeffery at call sites.
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Jarkko Sakkinen <jarkko@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
---
arch/x86/entry/calling.h | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
@@ -392,3 +392,21 @@ For 32-bit we have the following conventions - kernel is built with.endm#endif /* CONFIG_SMP */+/*+*ENDBRisaninstructionfortheIndirectBranchTracking(IBT)component+*ofCET.IBTpreventsattacksbyensuringthat(most)indirectbranches+*functioncallsmayonlylandatENDBRinstructions.Branchesthatdon't+*followtheruleswillresultincontrolflow(#CF)exceptions.+*ENDBRisanoopwhenIBTisunsupportedordisabled.MostENDBR+*instructionsareinsertedautomaticallybythecompiler,butbranch+*targetswritteninassemblymusthaveENDBRaddedmanually.+*/+.macroENDBR+#ifdef CONFIG_X86_CET+#ifdef __i386__+endbr32+#else+endbr64+#endif+#endif+.endm
@@ -392,3 +392,21 @@ For 32-bit we have the following conventions - kernel is built with.endm#endif /* CONFIG_SMP */+/*+*ENDBRisaninstructionfortheIndirectBranchTracking(IBT)component+*ofCET.IBTpreventsattacksbyensuringthat(most)indirectbranches+*functioncallsmayonlylandatENDBRinstructions.Branchesthatdon't+*followtheruleswillresultincontrolflow(#CF)exceptions.+*ENDBRisanoopwhenIBTisunsupportedordisabled.MostENDBR+*instructionsareinsertedautomaticallybythecompiler,butbranch+*targetswritteninassemblymusthaveENDBRaddedmanually.+*/+.macroENDBR+#ifdef CONFIG_X86_CET+#ifdef __i386__+endbr32+#else+endbr64+#endif+#endif+.endm
Is "#ifdef __i386__" the right thing to use here? I guess ENDBR only
ends up getting used in the VDSO, but there's a lot of
non-userspace-exposed stuff in calling.h. It seems a bit weird to have
the normally userspace-only __i386__ in there.
I don't see any existing direct use of __i386__ in arch/x86/entry/vdso.
@@ -392,3 +392,21 @@ For 32-bit we have the following conventions - kernel is built with.endm#endif /* CONFIG_SMP */+/*+*ENDBRisaninstructionfortheIndirectBranchTracking(IBT)component+*ofCET.IBTpreventsattacksbyensuringthat(most)indirectbranches+*functioncallsmayonlylandatENDBRinstructions.Branchesthatdon't+*followtheruleswillresultincontrolflow(#CF)exceptions.+*ENDBRisanoopwhenIBTisunsupportedordisabled.MostENDBR+*instructionsareinsertedautomaticallybythecompiler,butbranch+*targetswritteninassemblymusthaveENDBRaddedmanually.+*/+.macroENDBR+#ifdef CONFIG_X86_CET+#ifdef __i386__+endbr32+#else+endbr64+#endif+#endif+.endm
Is "#ifdef __i386__" the right thing to use here? I guess ENDBR only
ends up getting used in the VDSO, but there's a lot of
non-userspace-exposed stuff in calling.h. It seems a bit weird to have
the normally userspace-only __i386__ in there.
I don't see any existing direct use of __i386__ in arch/x86/entry/vdso.
Good point. My thought was, __i386__ comes from the compiler having the
-m32 command-line option, and it is not dependent on anything else.
Alternatively, there is another compiler-defined macro _CET_ENDBR that
can be used. We can put the following in calling.h:
#ifdef __CET__
#include <cet.h>
#else
#define _CET_ENDBR
#endif
and then use _CET_ENDBR in other files. How is that?
In the future, in case we have kernel-mode IBT, ENDBR macros are also
needed for other assembly files.
Thanks,
Yu-cheng
From: Dave Hansen <hidden> Date: 2021-03-16 17:29:46
On 3/16/21 10:12 AM, Yu, Yu-cheng wrote:
On 3/16/2021 8:49 AM, Dave Hansen wrote:
...
quoted
Is "#ifdef __i386__" the right thing to use here? I guess ENDBR only
ends up getting used in the VDSO, but there's a lot of
non-userspace-exposed stuff in calling.h. It seems a bit weird to have
the normally userspace-only __i386__ in there.
I don't see any existing direct use of __i386__ in arch/x86/entry/vdso.
Good point. My thought was, __i386__ comes from the compiler having the
-m32 command-line option, and it is not dependent on anything else.
Alternatively, there is another compiler-defined macro _CET_ENDBR that
can be used. We can put the following in calling.h:
#ifdef __CET__
#include <cet.h>
#else
#define _CET_ENDBR
#endif
and then use _CET_ENDBR in other files. How is that?
In the future, in case we have kernel-mode IBT, ENDBR macros are also
needed for other assembly files.
First of all, I think putting the macro in calling.h is wrong if it will
be used exclusively in the VDSO. If it's VDSO-only, please put it in a
local header in the vdso/ directory, maybe even a new cet.h.
Also, Boris asked for two *different* macros for 32 and 64-bit:
https://lore.kernel.org/linux-api/20210310231731.GK23521@zn.tnic/
Could you do that in the next version, please?
Is "#ifdef __i386__" the right thing to use here? I guess ENDBR only
ends up getting used in the VDSO, but there's a lot of
non-userspace-exposed stuff in calling.h. It seems a bit weird to have
the normally userspace-only __i386__ in there.
I don't see any existing direct use of __i386__ in arch/x86/entry/vdso.
Good point. My thought was, __i386__ comes from the compiler having the
-m32 command-line option, and it is not dependent on anything else.
Alternatively, there is another compiler-defined macro _CET_ENDBR that
can be used. We can put the following in calling.h:
#ifdef __CET__
#include <cet.h>
#else
#define _CET_ENDBR
#endif
and then use _CET_ENDBR in other files. How is that?
In the future, in case we have kernel-mode IBT, ENDBR macros are also
needed for other assembly files.
First of all, I think putting the macro in calling.h is wrong if it will
be used exclusively in the VDSO. If it's VDSO-only, please put it in a
local header in the vdso/ directory, maybe even a new cet.h.
Also, Boris asked for two *different* macros for 32 and 64-bit:
https://lore.kernel.org/linux-api/20210310231731.GK23521@zn.tnic/
Could you do that in the next version, please?
Yes, we can do two macros, probably in arch/x86/include/asm/vdso.h.
On Tue, Mar 16, 2021 at 10:12:39AM -0700, Yu, Yu-cheng wrote:
Alternatively, there is another compiler-defined macro _CET_ENDBR that can
be used. We can put the following in calling.h:
Not calling.h - this is apparently needed in vdso code only so I guess
some header there, arch/x86/include/asm/vdso.h maybe? In the
#else /* __ASSEMBLER__ */
branch maybe...
#ifdef __CET__
#include <cet.h>
#else
#define _CET_ENDBR
#endif
and then use _CET_ENDBR in other files. How is that?
On Tue, Mar 16, 2021 at 10:12:39AM -0700, Yu, Yu-cheng wrote:
quoted
Alternatively, there is another compiler-defined macro _CET_ENDBR that can
be used. We can put the following in calling.h:
Not calling.h - this is apparently needed in vdso code only so I guess
some header there, arch/x86/include/asm/vdso.h maybe? In the
#else /* __ASSEMBLER__ */
branch maybe...
quoted
#ifdef __CET__
#include <cet.h>
#else
#define _CET_ENDBR
#endif
and then use _CET_ENDBR in other files. How is that?
What does that macro do? Issue an ENDBR only?
Yes, issue endbr32, endbr64, or nothing when cet is not enabled.
From: Peter Zijlstra <peterz@infradead.org> Date: 2021-03-16 19:59:07
On Tue, Mar 16, 2021 at 10:12:39AM -0700, Yu, Yu-cheng wrote:
Alternatively, there is another compiler-defined macro _CET_ENDBR that can
be used. We can put the following in calling.h:
#ifdef __CET__
#include <cet.h>
#else
#define _CET_ENDBR
#endif
and then use _CET_ENDBR in other files. How is that?
In the future, in case we have kernel-mode IBT, ENDBR macros are also needed
for other assembly files.
Can we please call it IBT_ENDBR or just ENDBR. CET is a horrible name,
seeing how it is not specific.
On Tue, Mar 16, 2021 at 10:12:39AM -0700, Yu, Yu-cheng wrote:
quoted
Alternatively, there is another compiler-defined macro _CET_ENDBR that can
be used. We can put the following in calling.h:
#ifdef __CET__
#include <cet.h>
#else
#define _CET_ENDBR
#endif
and then use _CET_ENDBR in other files. How is that?
In the future, in case we have kernel-mode IBT, ENDBR macros are also needed
for other assembly files.
Can we please call it IBT_ENDBR or just ENDBR. CET is a horrible name,
seeing how it is not specific.
_CET_ENDBR is from the compiler and we cannot change it. We can do:
#define ENDBR _CET_ENDBR
How is that?
From: Peter Zijlstra <peterz@infradead.org> Date: 2021-03-16 20:17:00
On Tue, Mar 16, 2021 at 01:05:30PM -0700, Yu, Yu-cheng wrote:
On 3/16/2021 12:57 PM, Peter Zijlstra wrote:
quoted
On Tue, Mar 16, 2021 at 10:12:39AM -0700, Yu, Yu-cheng wrote:
quoted
Alternatively, there is another compiler-defined macro _CET_ENDBR that can
be used. We can put the following in calling.h:
#ifdef __CET__
#include <cet.h>
#else
#define _CET_ENDBR
#endif
and then use _CET_ENDBR in other files. How is that?
In the future, in case we have kernel-mode IBT, ENDBR macros are also needed
for other assembly files.
Can we please call it IBT_ENDBR or just ENDBR. CET is a horrible name,
seeing how it is not specific.
_CET_ENDBR is from the compiler and we cannot change it. We can do:
#define ENDBR _CET_ENDBR
How is that?
Do we really want to include compiler headers? AFAICT it's not a
built-in. Also what about clang?
This thing seems trivial enough to build our own, it's a single damn
instruction. That also means we don't have to worry about changes to
header files we don't control.
On Tue, Mar 16, 2021 at 01:05:30PM -0700, Yu, Yu-cheng wrote:
quoted
On 3/16/2021 12:57 PM, Peter Zijlstra wrote:
quoted
On Tue, Mar 16, 2021 at 10:12:39AM -0700, Yu, Yu-cheng wrote:
quoted
Alternatively, there is another compiler-defined macro _CET_ENDBR that can
be used. We can put the following in calling.h:
#ifdef __CET__
#include <cet.h>
#else
#define _CET_ENDBR
#endif
and then use _CET_ENDBR in other files. How is that?
In the future, in case we have kernel-mode IBT, ENDBR macros are also needed
for other assembly files.
Can we please call it IBT_ENDBR or just ENDBR. CET is a horrible name,
seeing how it is not specific.
_CET_ENDBR is from the compiler and we cannot change it. We can do:
#define ENDBR _CET_ENDBR
How is that?
Do we really want to include compiler headers? AFAICT it's not a
built-in. Also what about clang?
This thing seems trivial enough to build our own, it's a single damn
instruction. That also means we don't have to worry about changes to
header files we don't control.
Then, what about moving what I had earlier to vdso.h?
If we don't want __i386__ either, then make it two macros.
+.macro ENDBR
+#ifdef CONFIG_X86_CET
+#ifdef __i386__
+ endbr32
+#else
+ endbr64
+#endif
+#endif
+.endm
On Tue, Mar 16, 2021 at 01:05:30PM -0700, Yu, Yu-cheng wrote:
quoted
On 3/16/2021 12:57 PM, Peter Zijlstra wrote:
quoted
On Tue, Mar 16, 2021 at 10:12:39AM -0700, Yu, Yu-cheng wrote:
quoted
Alternatively, there is another compiler-defined macro _CET_ENDBR
that can
be used. We can put the following in calling.h:
#ifdef __CET__
#include <cet.h>
#else
#define _CET_ENDBR
#endif
and then use _CET_ENDBR in other files. How is that?
In the future, in case we have kernel-mode IBT, ENDBR macros are
also needed
for other assembly files.
Can we please call it IBT_ENDBR or just ENDBR. CET is a horrible name,
seeing how it is not specific.
_CET_ENDBR is from the compiler and we cannot change it. We can do:
#define ENDBR _CET_ENDBR
How is that?
Do we really want to include compiler headers? AFAICT it's not a
built-in. Also what about clang?
This thing seems trivial enough to build our own, it's a single damn
instruction. That also means we don't have to worry about changes to
header files we don't control.
Then, what about moving what I had earlier to vdso.h?
If we don't want __i386__ either, then make it two macros.
+.macro ENDBR
+#ifdef CONFIG_X86_CET
+#ifdef __i386__
+ endbr32
+#else
+ endbr64
+#endif
+#endif
+.endm
From: "H.J. Lu" <redacted>
ENDBR is a special new instruction for the Indirect Branch Tracking (IBT)
component of CET. IBT prevents attacks by ensuring that (most) indirect
branches and function calls may only land at ENDBR instructions. Branches
that don't follow the rules will result in control flow (#CF) exceptions.
ENDBR is a noop when IBT is unsupported or disabled. Most ENDBR
instructions are inserted automatically by the compiler, but branch
targets written in assembly must have ENDBR added manually.
Add that to __kernel_vsyscall entry point.
Signed-off-by: H.J. Lu <redacted>
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Kees Cook <redacted>
---
arch/x86/entry/vdso/vdso32/system_call.S | 2 ++
1 file changed, 2 insertions(+)
From: "H.J. Lu" <redacted>
When Indirect Branch Tracking (IBT) is enabled, vDSO functions may be
called indirectly, and must have ENDBR32 or ENDBR64 as the first
instruction. The compiler must support -fcf-protection=branch so that it
can be used to compile vDSO.
Signed-off-by: H.J. Lu <redacted>
Signed-off-by: Yu-cheng Yu <redacted>
Acked-by: Andy Lutomirski <luto@kernel.org>
Reviewed-by: Kees Cook <redacted>
---
arch/x86/entry/vdso/Makefile | 4 ++++
1 file changed, 4 insertions(+)
ENDBR is a special new instruction for the Indirect Branch Tracking (IBT)
component of CET. IBT prevents attacks by ensuring that (most) indirect
branches and function calls may only land at ENDBR instructions. Branches
that don't follow the rules will result in control flow (#CF) exceptions.
ENDBR is a noop when IBT is unsupported or disabled. Most ENDBR
instructions are inserted automatically by the compiler, but branch
targets written in assembly must have ENDBR added manually.
Add ENDBR to __vdso_sgx_enter_enclave() branch targets.
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Jarkko Sakkinen <jarkko@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
---
arch/x86/entry/vdso/vsgx.S | 4 ++++
1 file changed, 4 insertions(+)
From: Jarkko Sakkinen <jarkko@kernel.org> Date: 2021-03-16 19:23:53
On Tue, Mar 16, 2021 at 08:13:19AM -0700, Yu-cheng Yu wrote:
quoted hunk
ENDBR is a special new instruction for the Indirect Branch Tracking (IBT)
component of CET. IBT prevents attacks by ensuring that (most) indirect
branches and function calls may only land at ENDBR instructions. Branches
that don't follow the rules will result in control flow (#CF) exceptions.
ENDBR is a noop when IBT is unsupported or disabled. Most ENDBR
instructions are inserted automatically by the compiler, but branch
targets written in assembly must have ENDBR added manually.
Add ENDBR to __vdso_sgx_enter_enclave() branch targets.
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Jarkko Sakkinen <jarkko@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
---
arch/x86/entry/vdso/vsgx.S | 4 ++++
1 file changed, 4 insertions(+)
On Tue, Mar 16, 2021 at 08:13:19AM -0700, Yu-cheng Yu wrote:
quoted
ENDBR is a special new instruction for the Indirect Branch Tracking (IBT)
component of CET. IBT prevents attacks by ensuring that (most) indirect
branches and function calls may only land at ENDBR instructions. Branches
that don't follow the rules will result in control flow (#CF) exceptions.
ENDBR is a noop when IBT is unsupported or disabled. Most ENDBR
instructions are inserted automatically by the compiler, but branch
targets written in assembly must have ENDBR added manually.
Add ENDBR to __vdso_sgx_enter_enclave() branch targets.
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Jarkko Sakkinen <jarkko@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
---
arch/x86/entry/vdso/vsgx.S | 4 ++++
1 file changed, 4 insertions(+)
Looks good to me.
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
Thanks for reviewing. In response to Dave's and Boris' comments, I will
replace ENDBR macro with _CET_ENDBR that comes from the compiler. Can I
still keep the Reviewed-by?
From: Jarkko Sakkinen <jarkko@kernel.org> Date: 2021-03-17 01:43:19
On Tue, Mar 16, 2021 at 12:27:19PM -0700, Yu, Yu-cheng wrote:
On 3/16/2021 12:22 PM, Jarkko Sakkinen wrote:
quoted
On Tue, Mar 16, 2021 at 08:13:19AM -0700, Yu-cheng Yu wrote:
quoted
ENDBR is a special new instruction for the Indirect Branch Tracking (IBT)
component of CET. IBT prevents attacks by ensuring that (most) indirect
branches and function calls may only land at ENDBR instructions. Branches
that don't follow the rules will result in control flow (#CF) exceptions.
ENDBR is a noop when IBT is unsupported or disabled. Most ENDBR
instructions are inserted automatically by the compiler, but branch
targets written in assembly must have ENDBR added manually.
Add ENDBR to __vdso_sgx_enter_enclave() branch targets.
Signed-off-by: Yu-cheng Yu <redacted>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Jarkko Sakkinen <jarkko@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
---
arch/x86/entry/vdso/vsgx.S | 4 ++++
1 file changed, 4 insertions(+)
Looks good to me.
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
Thanks for reviewing. In response to Dave's and Boris' comments, I will
replace ENDBR macro with _CET_ENDBR that comes from the compiler. Can I
still keep the Reviewed-by?
I'll rather re-ack, thanks. Most likely give reviewed-by but I always
prefer to see the code change before doing that.
/Jarkko