]> git.baikalelectronics.ru Git - kernel.git/commitdiff
x86/sev-es: Use __copy_from_user_inatomic()
authorJoerg Roedel <jroedel@suse.de>
Wed, 3 Mar 2021 14:17:16 +0000 (15:17 +0100)
committerBorislav Petkov <bp@suse.de>
Tue, 9 Mar 2021 11:37:54 +0000 (12:37 +0100)
The #VC handler must run in atomic context and cannot sleep. This is a
problem when it tries to fetch instruction bytes from user-space via
copy_from_user().

Introduce a insn_fetch_from_user_inatomic() helper which uses
__copy_from_user_inatomic() to safely copy the instruction bytes to
kernel memory in the #VC handler.

Fixes: ca7508e3fe6f7 ("x86/sev-es: Handle instruction fetches from user-space")
Signed-off-by: Joerg Roedel <jroedel@suse.de>
Signed-off-by: Borislav Petkov <bp@suse.de>
Cc: stable@vger.kernel.org # v5.10+
Link: https://lkml.kernel.org/r/20210303141716.29223-6-joro@8bytes.org
arch/x86/include/asm/insn-eval.h
arch/x86/kernel/sev-es.c
arch/x86/lib/insn-eval.c

index a0f839aa144d9eeb347247e7f79e07683b5564f8..98b4dae5e8bc8a93398682080ab180982b33280f 100644 (file)
@@ -23,6 +23,8 @@ unsigned long insn_get_seg_base(struct pt_regs *regs, int seg_reg_idx);
 int insn_get_code_seg_params(struct pt_regs *regs);
 int insn_fetch_from_user(struct pt_regs *regs,
                         unsigned char buf[MAX_INSN_SIZE]);
+int insn_fetch_from_user_inatomic(struct pt_regs *regs,
+                                 unsigned char buf[MAX_INSN_SIZE]);
 bool insn_decode(struct insn *insn, struct pt_regs *regs,
                 unsigned char buf[MAX_INSN_SIZE], int buf_size);
 
index c3fd8fa79838e54d1eb1bc3a4296cd984b16517c..04a780abb512d01eb246067eff2b27f6e8501c51 100644 (file)
@@ -258,7 +258,7 @@ static enum es_result vc_decode_insn(struct es_em_ctxt *ctxt)
        int res;
 
        if (user_mode(ctxt->regs)) {
-               res = insn_fetch_from_user(ctxt->regs, buffer);
+               res = insn_fetch_from_user_inatomic(ctxt->regs, buffer);
                if (!res) {
                        ctxt->fi.vector     = X86_TRAP_PF;
                        ctxt->fi.error_code = X86_PF_INSTR | X86_PF_USER;
index 4229950a5d78c328a38f185119cc85098a36ce66..bb0b3fe1e0a0251c716ec2568576d8e0ceb13900 100644 (file)
@@ -1415,6 +1415,25 @@ void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)
        }
 }
 
+static unsigned long insn_get_effective_ip(struct pt_regs *regs)
+{
+       unsigned long seg_base = 0;
+
+       /*
+        * If not in user-space long mode, a custom code segment could be in
+        * use. This is true in protected mode (if the process defined a local
+        * descriptor table), or virtual-8086 mode. In most of the cases
+        * seg_base will be zero as in USER_CS.
+        */
+       if (!user_64bit_mode(regs)) {
+               seg_base = insn_get_seg_base(regs, INAT_SEG_REG_CS);
+               if (seg_base == -1L)
+                       return 0;
+       }
+
+       return seg_base + regs->ip;
+}
+
 /**
  * insn_fetch_from_user() - Copy instruction bytes from user-space memory
  * @regs:      Structure with register values as seen when entering kernel mode
@@ -1431,24 +1450,43 @@ void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)
  */
 int insn_fetch_from_user(struct pt_regs *regs, unsigned char buf[MAX_INSN_SIZE])
 {
-       unsigned long seg_base = 0;
+       unsigned long ip;
        int not_copied;
 
-       /*
-        * If not in user-space long mode, a custom code segment could be in
-        * use. This is true in protected mode (if the process defined a local
-        * descriptor table), or virtual-8086 mode. In most of the cases
-        * seg_base will be zero as in USER_CS.
-        */
-       if (!user_64bit_mode(regs)) {
-               seg_base = insn_get_seg_base(regs, INAT_SEG_REG_CS);
-               if (seg_base == -1L)
-                       return 0;
-       }
+       ip = insn_get_effective_ip(regs);
+       if (!ip)
+               return 0;
+
+       not_copied = copy_from_user(buf, (void __user *)ip, MAX_INSN_SIZE);
 
+       return MAX_INSN_SIZE - not_copied;
+}
+
+/**
+ * insn_fetch_from_user_inatomic() - Copy instruction bytes from user-space memory
+ *                                   while in atomic code
+ * @regs:      Structure with register values as seen when entering kernel mode
+ * @buf:       Array to store the fetched instruction
+ *
+ * Gets the linear address of the instruction and copies the instruction bytes
+ * to the buf. This function must be used in atomic context.
+ *
+ * Returns:
+ *
+ * Number of instruction bytes copied.
+ *
+ * 0 if nothing was copied.
+ */
+int insn_fetch_from_user_inatomic(struct pt_regs *regs, unsigned char buf[MAX_INSN_SIZE])
+{
+       unsigned long ip;
+       int not_copied;
+
+       ip = insn_get_effective_ip(regs);
+       if (!ip)
+               return 0;
 
-       not_copied = copy_from_user(buf, (void __user *)(seg_base + regs->ip),
-                                   MAX_INSN_SIZE);
+       not_copied = __copy_from_user_inatomic(buf, (void __user *)ip, MAX_INSN_SIZE);
 
        return MAX_INSN_SIZE - not_copied;
 }