|
Joerg Roedel |
8bcc6e |
From: Joerg Roedel <jroedel@suse.de>
|
|
Joerg Roedel |
8bcc6e |
Date: Wed, 3 Mar 2021 15:17:16 +0100
|
|
Joerg Roedel |
8bcc6e |
Subject: x86/sev-es: Use __copy_from_user_inatomic()
|
|
Joerg Roedel |
8bcc6e |
Git-commit: bffe30dd9f1f3b2608a87ac909a224d6be472485
|
|
Joerg Roedel |
8bcc6e |
Patch-mainline: v5.12-rc3
|
|
Joerg Roedel |
8bcc6e |
References: bsc#1183553
|
|
Joerg Roedel |
8bcc6e |
|
|
Joerg Roedel |
8bcc6e |
The #VC handler must run in atomic context and cannot sleep. This is a
|
|
Joerg Roedel |
8bcc6e |
problem when it tries to fetch instruction bytes from user-space via
|
|
Joerg Roedel |
8bcc6e |
copy_from_user().
|
|
Joerg Roedel |
8bcc6e |
|
|
Joerg Roedel |
8bcc6e |
Introduce a insn_fetch_from_user_inatomic() helper which uses
|
|
Joerg Roedel |
8bcc6e |
__copy_from_user_inatomic() to safely copy the instruction bytes to
|
|
Joerg Roedel |
8bcc6e |
kernel memory in the #VC handler.
|
|
Joerg Roedel |
8bcc6e |
|
|
Joerg Roedel |
8bcc6e |
Fixes: 5e3427a7bc432 ("x86/sev-es: Handle instruction fetches from user-space")
|
|
Joerg Roedel |
8bcc6e |
Signed-off-by: Joerg Roedel <jroedel@suse.de>
|
|
Joerg Roedel |
8bcc6e |
Signed-off-by: Borislav Petkov <bp@suse.de>
|
|
Joerg Roedel |
8bcc6e |
Cc: stable@vger.kernel.org # v5.10+
|
|
Joerg Roedel |
8bcc6e |
Link: https://lkml.kernel.org/r/20210303141716.29223-6-joro@8bytes.org
|
|
Joerg Roedel |
8bcc6e |
---
|
|
Joerg Roedel |
8bcc6e |
arch/x86/include/asm/insn-eval.h | 2 +
|
|
Joerg Roedel |
8bcc6e |
arch/x86/kernel/sev-es.c | 2 -
|
|
Joerg Roedel |
8bcc6e |
arch/x86/lib/insn-eval.c | 66 ++++++++++++++++++++++++++++++---------
|
|
Joerg Roedel |
8bcc6e |
3 files changed, 55 insertions(+), 15 deletions(-)
|
|
Joerg Roedel |
8bcc6e |
|
|
Joerg Roedel |
8bcc6e |
--- a/arch/x86/include/asm/insn-eval.h
|
|
Joerg Roedel |
8bcc6e |
+++ b/arch/x86/include/asm/insn-eval.h
|
|
Joerg Roedel |
8bcc6e |
@@ -23,6 +23,8 @@ unsigned long insn_get_seg_base(struct p
|
|
Joerg Roedel |
8bcc6e |
int insn_get_code_seg_params(struct pt_regs *regs);
|
|
Joerg Roedel |
8bcc6e |
int insn_fetch_from_user(struct pt_regs *regs,
|
|
Joerg Roedel |
8bcc6e |
unsigned char buf[MAX_INSN_SIZE]);
|
|
Joerg Roedel |
8bcc6e |
+int insn_fetch_from_user_inatomic(struct pt_regs *regs,
|
|
Joerg Roedel |
8bcc6e |
+ unsigned char buf[MAX_INSN_SIZE]);
|
|
Joerg Roedel |
8bcc6e |
bool insn_decode(struct insn *insn, struct pt_regs *regs,
|
|
Joerg Roedel |
8bcc6e |
unsigned char buf[MAX_INSN_SIZE], int buf_size);
|
|
Joerg Roedel |
8bcc6e |
|
|
Joerg Roedel |
8bcc6e |
--- a/arch/x86/kernel/sev-es.c
|
|
Joerg Roedel |
8bcc6e |
+++ b/arch/x86/kernel/sev-es.c
|
|
Joerg Roedel |
8bcc6e |
@@ -260,7 +260,7 @@ static enum es_result vc_decode_insn(str
|
|
Joerg Roedel |
8bcc6e |
int res;
|
|
Joerg Roedel |
8bcc6e |
|
|
Joerg Roedel |
8bcc6e |
if (user_mode(ctxt->regs)) {
|
|
Joerg Roedel |
8bcc6e |
- res = insn_fetch_from_user(ctxt->regs, buffer);
|
|
Joerg Roedel |
8bcc6e |
+ res = insn_fetch_from_user_inatomic(ctxt->regs, buffer);
|
|
Joerg Roedel |
8bcc6e |
if (!res) {
|
|
Joerg Roedel |
8bcc6e |
ctxt->fi.vector = X86_TRAP_PF;
|
|
Joerg Roedel |
8bcc6e |
ctxt->fi.error_code = X86_PF_INSTR | X86_PF_USER;
|
|
Joerg Roedel |
8bcc6e |
--- a/arch/x86/lib/insn-eval.c
|
|
Joerg Roedel |
8bcc6e |
+++ b/arch/x86/lib/insn-eval.c
|
|
Joerg Roedel |
8bcc6e |
@@ -1417,6 +1417,25 @@ void __user *insn_get_addr_ref(struct in
|
|
Joerg Roedel |
8bcc6e |
}
|
|
Joerg Roedel |
8bcc6e |
}
|
|
Joerg Roedel |
8bcc6e |
|
|
Joerg Roedel |
8bcc6e |
+static unsigned long insn_get_effective_ip(struct pt_regs *regs)
|
|
Joerg Roedel |
8bcc6e |
+{
|
|
Joerg Roedel |
8bcc6e |
+ unsigned long seg_base = 0;
|
|
Joerg Roedel |
8bcc6e |
+
|
|
Joerg Roedel |
8bcc6e |
+ /*
|
|
Joerg Roedel |
8bcc6e |
+ * If not in user-space long mode, a custom code segment could be in
|
|
Joerg Roedel |
8bcc6e |
+ * use. This is true in protected mode (if the process defined a local
|
|
Joerg Roedel |
8bcc6e |
+ * descriptor table), or virtual-8086 mode. In most of the cases
|
|
Joerg Roedel |
8bcc6e |
+ * seg_base will be zero as in USER_CS.
|
|
Joerg Roedel |
8bcc6e |
+ */
|
|
Joerg Roedel |
8bcc6e |
+ if (!user_64bit_mode(regs)) {
|
|
Joerg Roedel |
8bcc6e |
+ seg_base = insn_get_seg_base(regs, INAT_SEG_REG_CS);
|
|
Joerg Roedel |
8bcc6e |
+ if (seg_base == -1L)
|
|
Joerg Roedel |
8bcc6e |
+ return 0;
|
|
Joerg Roedel |
8bcc6e |
+ }
|
|
Joerg Roedel |
8bcc6e |
+
|
|
Joerg Roedel |
8bcc6e |
+ return seg_base + regs->ip;
|
|
Joerg Roedel |
8bcc6e |
+}
|
|
Joerg Roedel |
8bcc6e |
+
|
|
Joerg Roedel |
8bcc6e |
/**
|
|
Joerg Roedel |
8bcc6e |
* insn_fetch_from_user() - Copy instruction bytes from user-space memory
|
|
Joerg Roedel |
8bcc6e |
* @regs: Structure with register values as seen when entering kernel mode
|
|
Joerg Roedel |
8bcc6e |
@@ -1433,24 +1452,43 @@ void __user *insn_get_addr_ref(struct in
|
|
Joerg Roedel |
8bcc6e |
*/
|
|
Joerg Roedel |
8bcc6e |
int insn_fetch_from_user(struct pt_regs *regs, unsigned char buf[MAX_INSN_SIZE])
|
|
Joerg Roedel |
8bcc6e |
{
|
|
Joerg Roedel |
8bcc6e |
- unsigned long seg_base = 0;
|
|
Joerg Roedel |
8bcc6e |
+ unsigned long ip;
|
|
Joerg Roedel |
8bcc6e |
int not_copied;
|
|
Joerg Roedel |
8bcc6e |
|
|
Joerg Roedel |
8bcc6e |
- /*
|
|
Joerg Roedel |
8bcc6e |
- * If not in user-space long mode, a custom code segment could be in
|
|
Joerg Roedel |
8bcc6e |
- * use. This is true in protected mode (if the process defined a local
|
|
Joerg Roedel |
8bcc6e |
- * descriptor table), or virtual-8086 mode. In most of the cases
|
|
Joerg Roedel |
8bcc6e |
- * seg_base will be zero as in USER_CS.
|
|
Joerg Roedel |
8bcc6e |
- */
|
|
Joerg Roedel |
8bcc6e |
- if (!user_64bit_mode(regs)) {
|
|
Joerg Roedel |
8bcc6e |
- seg_base = insn_get_seg_base(regs, INAT_SEG_REG_CS);
|
|
Joerg Roedel |
8bcc6e |
- if (seg_base == -1L)
|
|
Joerg Roedel |
8bcc6e |
- return 0;
|
|
Joerg Roedel |
8bcc6e |
- }
|
|
Joerg Roedel |
8bcc6e |
+ ip = insn_get_effective_ip(regs);
|
|
Joerg Roedel |
8bcc6e |
+ if (!ip)
|
|
Joerg Roedel |
8bcc6e |
+ return 0;
|
|
Joerg Roedel |
8bcc6e |
+
|
|
Joerg Roedel |
8bcc6e |
+ not_copied = copy_from_user(buf, (void __user *)ip, MAX_INSN_SIZE);
|
|
Joerg Roedel |
8bcc6e |
+
|
|
Joerg Roedel |
8bcc6e |
+ return MAX_INSN_SIZE - not_copied;
|
|
Joerg Roedel |
8bcc6e |
+}
|
|
Joerg Roedel |
8bcc6e |
+
|
|
Joerg Roedel |
8bcc6e |
+/**
|
|
Joerg Roedel |
8bcc6e |
+ * insn_fetch_from_user_inatomic() - Copy instruction bytes from user-space memory
|
|
Joerg Roedel |
8bcc6e |
+ * while in atomic code
|
|
Joerg Roedel |
8bcc6e |
+ * @regs: Structure with register values as seen when entering kernel mode
|
|
Joerg Roedel |
8bcc6e |
+ * @buf: Array to store the fetched instruction
|
|
Joerg Roedel |
8bcc6e |
+ *
|
|
Joerg Roedel |
8bcc6e |
+ * Gets the linear address of the instruction and copies the instruction bytes
|
|
Joerg Roedel |
8bcc6e |
+ * to the buf. This function must be used in atomic context.
|
|
Joerg Roedel |
8bcc6e |
+ *
|
|
Joerg Roedel |
8bcc6e |
+ * Returns:
|
|
Joerg Roedel |
8bcc6e |
+ *
|
|
Joerg Roedel |
8bcc6e |
+ * Number of instruction bytes copied.
|
|
Joerg Roedel |
8bcc6e |
+ *
|
|
Joerg Roedel |
8bcc6e |
+ * 0 if nothing was copied.
|
|
Joerg Roedel |
8bcc6e |
+ */
|
|
Joerg Roedel |
8bcc6e |
+int insn_fetch_from_user_inatomic(struct pt_regs *regs, unsigned char buf[MAX_INSN_SIZE])
|
|
Joerg Roedel |
8bcc6e |
+{
|
|
Joerg Roedel |
8bcc6e |
+ unsigned long ip;
|
|
Joerg Roedel |
8bcc6e |
+ int not_copied;
|
|
Joerg Roedel |
8bcc6e |
|
|
Joerg Roedel |
8bcc6e |
+ ip = insn_get_effective_ip(regs);
|
|
Joerg Roedel |
8bcc6e |
+ if (!ip)
|
|
Joerg Roedel |
8bcc6e |
+ return 0;
|
|
Joerg Roedel |
8bcc6e |
|
|
Joerg Roedel |
8bcc6e |
- not_copied = copy_from_user(buf, (void __user *)(seg_base + regs->ip),
|
|
Joerg Roedel |
8bcc6e |
- MAX_INSN_SIZE);
|
|
Joerg Roedel |
8bcc6e |
+ not_copied = __copy_from_user_inatomic(buf, (void __user *)ip, MAX_INSN_SIZE);
|
|
Joerg Roedel |
8bcc6e |
|
|
Joerg Roedel |
8bcc6e |
return MAX_INSN_SIZE - not_copied;
|
|
Joerg Roedel |
8bcc6e |
}
|
|
Joerg Roedel |
8bcc6e |
|