[PATCH v3 7/7] x86/tdx: Avoid crossing the page boundary

From: Alexey Gladkov (Intel)
Date: Thu Aug 08 2024 - 11:44:59 EST


In case the instruction is close to the page boundary, reading
MAX_INSN_SIZE may cross the page boundary. The second page might be
from a different VMA and reading can have side effects.

The problem is that the actual size of the instruction is not known.

The solution might be to try read the data to the end of the page and
try parse it in the hope that the instruction is smaller than the
maximum buffer size.

Co-developed-by: Kirill A. Shutemov <kirill.shutemov@xxxxxxxxxxxxxxx>
Signed-off-by: Alexey Gladkov (Intel) <legion@xxxxxxxxxx>
---
arch/x86/coco/tdx/tdx.c | 30 +++++++++++++++++++++++++-----
1 file changed, 25 insertions(+), 5 deletions(-)

diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c
index 5b3421a89998..ea3df77feef0 100644
--- a/arch/x86/coco/tdx/tdx.c
+++ b/arch/x86/coco/tdx/tdx.c
@@ -494,16 +494,32 @@ static int decode_insn_struct(struct insn *insn, struct pt_regs *regs)
char buffer[MAX_INSN_SIZE];

if (user_mode(regs)) {
- int nr_copied = insn_fetch_from_user(regs, buffer);
+ int nr_copied, size;
+ unsigned long ip;

- if (nr_copied <= 0)
+ if (insn_get_effective_ip(regs, &ip))
return -EFAULT;

- if (!insn_decode_from_regs(insn, regs, buffer, nr_copied))
- return -EINVAL;
+ /*
+ * On the first attempt, read up to MAX_INSN_SIZE, but do not cross a
+ * page boundary. The second page might be from a different VMA and
+ * reading can have side effects (i.e. reading from MMIO).
+ */
+ size = min(MAX_INSN_SIZE, PAGE_SIZE - offset_in_page(ip));
+retry:
+ nr_copied = size - copy_from_user(buffer, (void __user *)ip, size);
+
+ if (nr_copied <= 0)
+ return -EFAULT;

- if (!insn->immediate.got)
+ if (!insn_decode_from_regs(insn, regs, buffer, nr_copied)) {
+ /* If decode failed, try to copy across page boundary */
+ if (size < MAX_INSN_SIZE) {
+ size = MAX_INSN_SIZE;
+ goto retry;
+ }
return -EINVAL;
+ }
} else {
if (copy_from_kernel_nofault(buffer, (void *)regs->ip, MAX_INSN_SIZE))
return -EFAULT;
@@ -511,6 +527,10 @@ static int decode_insn_struct(struct insn *insn, struct pt_regs *regs)
if (insn_decode(insn, buffer, MAX_INSN_SIZE, INSN_MODE_64))
return -EINVAL;
}
+
+ if (!insn->immediate.got)
+ return -EINVAL;
+
return 0;
}

--
2.45.2