diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2018-11-25 13:33:53 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2018-11-28 05:57:13 -0500 |
commit | 9137bb27e60e554dab694eafa4cca241fa3a694f (patch) | |
tree | 210ca811947bb90452df9f8a3752402e6252a530 | |
parent | 6893a959d7fdebbab5f5aa112c277d5a44435ba1 (diff) |
x86/speculation: Add prctl() control for indirect branch speculation
Add the PR_SPEC_INDIRECT_BRANCH option for the PR_GET_SPECULATION_CTRL and
PR_SET_SPECULATION_CTRL prctls to allow fine grained per task control of
indirect branch speculation via STIBP and IBPB.
Invocations:
Check indirect branch speculation status with
- prctl(PR_GET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, 0, 0, 0);
Enable indirect branch speculation with
- prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, PR_SPEC_ENABLE, 0, 0);
Disable indirect branch speculation with
- prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, PR_SPEC_DISABLE, 0, 0);
Force disable indirect branch speculation with
- prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, PR_SPEC_FORCE_DISABLE, 0, 0);
See Documentation/userspace-api/spec_ctrl.rst.
Signed-off-by: Tim Chen <tim.c.chen@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Ingo Molnar <mingo@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Jiri Kosina <jkosina@suse.cz>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Cc: David Woodhouse <dwmw@amazon.co.uk>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Dave Hansen <dave.hansen@intel.com>
Cc: Casey Schaufler <casey.schaufler@intel.com>
Cc: Asit Mallick <asit.k.mallick@intel.com>
Cc: Arjan van de Ven <arjan@linux.intel.com>
Cc: Jon Masters <jcm@redhat.com>
Cc: Waiman Long <longman9394@gmail.com>
Cc: Greg KH <gregkh@linuxfoundation.org>
Cc: Dave Stewart <david.c.stewart@intel.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: stable@vger.kernel.org
Link: https://lkml.kernel.org/r/20181125185005.866780996@linutronix.de
-rw-r--r-- | Documentation/userspace-api/spec_ctrl.rst | 9 | ||||
-rw-r--r-- | arch/x86/include/asm/nospec-branch.h | 1 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/bugs.c | 67 | ||||
-rw-r--r-- | arch/x86/kernel/process.c | 5 | ||||
-rw-r--r-- | include/linux/sched.h | 9 | ||||
-rw-r--r-- | include/uapi/linux/prctl.h | 1 | ||||
-rw-r--r-- | tools/include/uapi/linux/prctl.h | 1 |
7 files changed, 93 insertions, 0 deletions
diff --git a/Documentation/userspace-api/spec_ctrl.rst b/Documentation/userspace-api/spec_ctrl.rst index 32f3d55c54b7..c4dbe6f7cdae 100644 --- a/Documentation/userspace-api/spec_ctrl.rst +++ b/Documentation/userspace-api/spec_ctrl.rst | |||
@@ -92,3 +92,12 @@ Speculation misfeature controls | |||
92 | * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_ENABLE, 0, 0); | 92 | * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_ENABLE, 0, 0); |
93 | * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_DISABLE, 0, 0); | 93 | * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_DISABLE, 0, 0); |
94 | * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_FORCE_DISABLE, 0, 0); | 94 | * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_FORCE_DISABLE, 0, 0); |
95 | |||
96 | - PR_SPEC_INDIR_BRANCH: Indirect Branch Speculation in User Processes | ||
97 | (Mitigate Spectre V2 style attacks against user processes) | ||
98 | |||
99 | Invocations: | ||
100 | * prctl(PR_GET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, 0, 0, 0); | ||
101 | * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, PR_SPEC_ENABLE, 0, 0); | ||
102 | * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, PR_SPEC_DISABLE, 0, 0); | ||
103 | * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, PR_SPEC_FORCE_DISABLE, 0, 0); | ||
diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h index d4d35baf0430..2adbe7b047fa 100644 --- a/arch/x86/include/asm/nospec-branch.h +++ b/arch/x86/include/asm/nospec-branch.h | |||
@@ -232,6 +232,7 @@ enum spectre_v2_mitigation { | |||
232 | enum spectre_v2_user_mitigation { | 232 | enum spectre_v2_user_mitigation { |
233 | SPECTRE_V2_USER_NONE, | 233 | SPECTRE_V2_USER_NONE, |
234 | SPECTRE_V2_USER_STRICT, | 234 | SPECTRE_V2_USER_STRICT, |
235 | SPECTRE_V2_USER_PRCTL, | ||
235 | }; | 236 | }; |
236 | 237 | ||
237 | /* The Speculative Store Bypass disable variants */ | 238 | /* The Speculative Store Bypass disable variants */ |
diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c index 9cab538e10f1..74359fff87fd 100644 --- a/arch/x86/kernel/cpu/bugs.c +++ b/arch/x86/kernel/cpu/bugs.c | |||
@@ -566,6 +566,8 @@ void arch_smt_update(void) | |||
566 | case SPECTRE_V2_USER_STRICT: | 566 | case SPECTRE_V2_USER_STRICT: |
567 | update_stibp_strict(); | 567 | update_stibp_strict(); |
568 | break; | 568 | break; |
569 | case SPECTRE_V2_USER_PRCTL: | ||
570 | break; | ||
569 | } | 571 | } |
570 | 572 | ||
571 | mutex_unlock(&spec_ctrl_mutex); | 573 | mutex_unlock(&spec_ctrl_mutex); |
@@ -752,12 +754,50 @@ static int ssb_prctl_set(struct task_struct *task, unsigned long ctrl) | |||
752 | return 0; | 754 | return 0; |
753 | } | 755 | } |
754 | 756 | ||
757 | static int ib_prctl_set(struct task_struct *task, unsigned long ctrl) | ||
758 | { | ||
759 | switch (ctrl) { | ||
760 | case PR_SPEC_ENABLE: | ||
761 | if (spectre_v2_user == SPECTRE_V2_USER_NONE) | ||
762 | return 0; | ||
763 | /* | ||
764 | * Indirect branch speculation is always disabled in strict | ||
765 | * mode. | ||
766 | */ | ||
767 | if (spectre_v2_user == SPECTRE_V2_USER_STRICT) | ||
768 | return -EPERM; | ||
769 | task_clear_spec_ib_disable(task); | ||
770 | task_update_spec_tif(task); | ||
771 | break; | ||
772 | case PR_SPEC_DISABLE: | ||
773 | case PR_SPEC_FORCE_DISABLE: | ||
774 | /* | ||
775 | * Indirect branch speculation is always allowed when | ||
776 | * mitigation is force disabled. | ||
777 | */ | ||
778 | if (spectre_v2_user == SPECTRE_V2_USER_NONE) | ||
779 | return -EPERM; | ||
780 | if (spectre_v2_user == SPECTRE_V2_USER_STRICT) | ||
781 | return 0; | ||
782 | task_set_spec_ib_disable(task); | ||
783 | if (ctrl == PR_SPEC_FORCE_DISABLE) | ||
784 | task_set_spec_ib_force_disable(task); | ||
785 | task_update_spec_tif(task); | ||
786 | break; | ||
787 | default: | ||
788 | return -ERANGE; | ||
789 | } | ||
790 | return 0; | ||
791 | } | ||
792 | |||
755 | int arch_prctl_spec_ctrl_set(struct task_struct *task, unsigned long which, | 793 | int arch_prctl_spec_ctrl_set(struct task_struct *task, unsigned long which, |
756 | unsigned long ctrl) | 794 | unsigned long ctrl) |
757 | { | 795 | { |
758 | switch (which) { | 796 | switch (which) { |
759 | case PR_SPEC_STORE_BYPASS: | 797 | case PR_SPEC_STORE_BYPASS: |
760 | return ssb_prctl_set(task, ctrl); | 798 | return ssb_prctl_set(task, ctrl); |
799 | case PR_SPEC_INDIRECT_BRANCH: | ||
800 | return ib_prctl_set(task, ctrl); | ||
761 | default: | 801 | default: |
762 | return -ENODEV; | 802 | return -ENODEV; |
763 | } | 803 | } |
@@ -790,11 +830,34 @@ static int ssb_prctl_get(struct task_struct *task) | |||
790 | } | 830 | } |
791 | } | 831 | } |
792 | 832 | ||
833 | static int ib_prctl_get(struct task_struct *task) | ||
834 | { | ||
835 | if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2)) | ||
836 | return PR_SPEC_NOT_AFFECTED; | ||
837 | |||
838 | switch (spectre_v2_user) { | ||
839 | case SPECTRE_V2_USER_NONE: | ||
840 | return PR_SPEC_ENABLE; | ||
841 | case SPECTRE_V2_USER_PRCTL: | ||
842 | if (task_spec_ib_force_disable(task)) | ||
843 | return PR_SPEC_PRCTL | PR_SPEC_FORCE_DISABLE; | ||
844 | if (task_spec_ib_disable(task)) | ||
845 | return PR_SPEC_PRCTL | PR_SPEC_DISABLE; | ||
846 | return PR_SPEC_PRCTL | PR_SPEC_ENABLE; | ||
847 | case SPECTRE_V2_USER_STRICT: | ||
848 | return PR_SPEC_DISABLE; | ||
849 | default: | ||
850 | return PR_SPEC_NOT_AFFECTED; | ||
851 | } | ||
852 | } | ||
853 | |||
793 | int arch_prctl_spec_ctrl_get(struct task_struct *task, unsigned long which) | 854 | int arch_prctl_spec_ctrl_get(struct task_struct *task, unsigned long which) |
794 | { | 855 | { |
795 | switch (which) { | 856 | switch (which) { |
796 | case PR_SPEC_STORE_BYPASS: | 857 | case PR_SPEC_STORE_BYPASS: |
797 | return ssb_prctl_get(task); | 858 | return ssb_prctl_get(task); |
859 | case PR_SPEC_INDIRECT_BRANCH: | ||
860 | return ib_prctl_get(task); | ||
798 | default: | 861 | default: |
799 | return -ENODEV; | 862 | return -ENODEV; |
800 | } | 863 | } |
@@ -974,6 +1037,8 @@ static char *stibp_state(void) | |||
974 | return ", STIBP: disabled"; | 1037 | return ", STIBP: disabled"; |
975 | case SPECTRE_V2_USER_STRICT: | 1038 | case SPECTRE_V2_USER_STRICT: |
976 | return ", STIBP: forced"; | 1039 | return ", STIBP: forced"; |
1040 | case SPECTRE_V2_USER_PRCTL: | ||
1041 | return ""; | ||
977 | } | 1042 | } |
978 | return ""; | 1043 | return ""; |
979 | } | 1044 | } |
@@ -986,6 +1051,8 @@ static char *ibpb_state(void) | |||
986 | return ", IBPB: disabled"; | 1051 | return ", IBPB: disabled"; |
987 | case SPECTRE_V2_USER_STRICT: | 1052 | case SPECTRE_V2_USER_STRICT: |
988 | return ", IBPB: always-on"; | 1053 | return ", IBPB: always-on"; |
1054 | case SPECTRE_V2_USER_PRCTL: | ||
1055 | return ""; | ||
989 | } | 1056 | } |
990 | } | 1057 | } |
991 | return ""; | 1058 | return ""; |
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index afbe2eb4a1c6..7d31192296a8 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c | |||
@@ -450,6 +450,11 @@ static unsigned long speculation_ctrl_update_tif(struct task_struct *tsk) | |||
450 | set_tsk_thread_flag(tsk, TIF_SSBD); | 450 | set_tsk_thread_flag(tsk, TIF_SSBD); |
451 | else | 451 | else |
452 | clear_tsk_thread_flag(tsk, TIF_SSBD); | 452 | clear_tsk_thread_flag(tsk, TIF_SSBD); |
453 | |||
454 | if (task_spec_ib_disable(tsk)) | ||
455 | set_tsk_thread_flag(tsk, TIF_SPEC_IB); | ||
456 | else | ||
457 | clear_tsk_thread_flag(tsk, TIF_SPEC_IB); | ||
453 | } | 458 | } |
454 | /* Return the updated threadinfo flags*/ | 459 | /* Return the updated threadinfo flags*/ |
455 | return task_thread_info(tsk)->flags; | 460 | return task_thread_info(tsk)->flags; |
diff --git a/include/linux/sched.h b/include/linux/sched.h index a51c13c2b1a0..d607db5fcc6a 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
@@ -1453,6 +1453,8 @@ static inline bool is_percpu_thread(void) | |||
1453 | #define PFA_SPREAD_SLAB 2 /* Spread some slab caches over cpuset */ | 1453 | #define PFA_SPREAD_SLAB 2 /* Spread some slab caches over cpuset */ |
1454 | #define PFA_SPEC_SSB_DISABLE 3 /* Speculative Store Bypass disabled */ | 1454 | #define PFA_SPEC_SSB_DISABLE 3 /* Speculative Store Bypass disabled */ |
1455 | #define PFA_SPEC_SSB_FORCE_DISABLE 4 /* Speculative Store Bypass force disabled*/ | 1455 | #define PFA_SPEC_SSB_FORCE_DISABLE 4 /* Speculative Store Bypass force disabled*/ |
1456 | #define PFA_SPEC_IB_DISABLE 5 /* Indirect branch speculation restricted */ | ||
1457 | #define PFA_SPEC_IB_FORCE_DISABLE 6 /* Indirect branch speculation permanently restricted */ | ||
1456 | 1458 | ||
1457 | #define TASK_PFA_TEST(name, func) \ | 1459 | #define TASK_PFA_TEST(name, func) \ |
1458 | static inline bool task_##func(struct task_struct *p) \ | 1460 | static inline bool task_##func(struct task_struct *p) \ |
@@ -1484,6 +1486,13 @@ TASK_PFA_CLEAR(SPEC_SSB_DISABLE, spec_ssb_disable) | |||
1484 | TASK_PFA_TEST(SPEC_SSB_FORCE_DISABLE, spec_ssb_force_disable) | 1486 | TASK_PFA_TEST(SPEC_SSB_FORCE_DISABLE, spec_ssb_force_disable) |
1485 | TASK_PFA_SET(SPEC_SSB_FORCE_DISABLE, spec_ssb_force_disable) | 1487 | TASK_PFA_SET(SPEC_SSB_FORCE_DISABLE, spec_ssb_force_disable) |
1486 | 1488 | ||
1489 | TASK_PFA_TEST(SPEC_IB_DISABLE, spec_ib_disable) | ||
1490 | TASK_PFA_SET(SPEC_IB_DISABLE, spec_ib_disable) | ||
1491 | TASK_PFA_CLEAR(SPEC_IB_DISABLE, spec_ib_disable) | ||
1492 | |||
1493 | TASK_PFA_TEST(SPEC_IB_FORCE_DISABLE, spec_ib_force_disable) | ||
1494 | TASK_PFA_SET(SPEC_IB_FORCE_DISABLE, spec_ib_force_disable) | ||
1495 | |||
1487 | static inline void | 1496 | static inline void |
1488 | current_restore_flags(unsigned long orig_flags, unsigned long flags) | 1497 | current_restore_flags(unsigned long orig_flags, unsigned long flags) |
1489 | { | 1498 | { |
diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h index c0d7ea0bf5b6..b17201edfa09 100644 --- a/include/uapi/linux/prctl.h +++ b/include/uapi/linux/prctl.h | |||
@@ -212,6 +212,7 @@ struct prctl_mm_map { | |||
212 | #define PR_SET_SPECULATION_CTRL 53 | 212 | #define PR_SET_SPECULATION_CTRL 53 |
213 | /* Speculation control variants */ | 213 | /* Speculation control variants */ |
214 | # define PR_SPEC_STORE_BYPASS 0 | 214 | # define PR_SPEC_STORE_BYPASS 0 |
215 | # define PR_SPEC_INDIRECT_BRANCH 1 | ||
215 | /* Return and control values for PR_SET/GET_SPECULATION_CTRL */ | 216 | /* Return and control values for PR_SET/GET_SPECULATION_CTRL */ |
216 | # define PR_SPEC_NOT_AFFECTED 0 | 217 | # define PR_SPEC_NOT_AFFECTED 0 |
217 | # define PR_SPEC_PRCTL (1UL << 0) | 218 | # define PR_SPEC_PRCTL (1UL << 0) |
diff --git a/tools/include/uapi/linux/prctl.h b/tools/include/uapi/linux/prctl.h index c0d7ea0bf5b6..b17201edfa09 100644 --- a/tools/include/uapi/linux/prctl.h +++ b/tools/include/uapi/linux/prctl.h | |||
@@ -212,6 +212,7 @@ struct prctl_mm_map { | |||
212 | #define PR_SET_SPECULATION_CTRL 53 | 212 | #define PR_SET_SPECULATION_CTRL 53 |
213 | /* Speculation control variants */ | 213 | /* Speculation control variants */ |
214 | # define PR_SPEC_STORE_BYPASS 0 | 214 | # define PR_SPEC_STORE_BYPASS 0 |
215 | # define PR_SPEC_INDIRECT_BRANCH 1 | ||
215 | /* Return and control values for PR_SET/GET_SPECULATION_CTRL */ | 216 | /* Return and control values for PR_SET/GET_SPECULATION_CTRL */ |
216 | # define PR_SPEC_NOT_AFFECTED 0 | 217 | # define PR_SPEC_NOT_AFFECTED 0 |
217 | # define PR_SPEC_PRCTL (1UL << 0) | 218 | # define PR_SPEC_PRCTL (1UL << 0) |