Skip to content

Commit bffe30d

Browse files
joergroedelsuryasaimadhu
authored andcommitted
x86/sev-es: Use __copy_from_user_inatomic()
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: 5e3427a ("x86/sev-es: Handle instruction fetches from user-space") Signed-off-by: Joerg Roedel <[email protected]> Signed-off-by: Borislav Petkov <[email protected]> Cc: [email protected] # v5.10+ Link: https://lkml.kernel.org/r/[email protected]
1 parent 62441a1 commit bffe30d

File tree

3 files changed

+55
-15
lines changed

3 files changed

+55
-15
lines changed

arch/x86/include/asm/insn-eval.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ unsigned long insn_get_seg_base(struct pt_regs *regs, int seg_reg_idx);
2323
int insn_get_code_seg_params(struct pt_regs *regs);
2424
int insn_fetch_from_user(struct pt_regs *regs,
2525
unsigned char buf[MAX_INSN_SIZE]);
26+
int insn_fetch_from_user_inatomic(struct pt_regs *regs,
27+
unsigned char buf[MAX_INSN_SIZE]);
2628
bool insn_decode(struct insn *insn, struct pt_regs *regs,
2729
unsigned char buf[MAX_INSN_SIZE], int buf_size);
2830

arch/x86/kernel/sev-es.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ static enum es_result vc_decode_insn(struct es_em_ctxt *ctxt)
258258
int res;
259259

260260
if (user_mode(ctxt->regs)) {
261-
res = insn_fetch_from_user(ctxt->regs, buffer);
261+
res = insn_fetch_from_user_inatomic(ctxt->regs, buffer);
262262
if (!res) {
263263
ctxt->fi.vector = X86_TRAP_PF;
264264
ctxt->fi.error_code = X86_PF_INSTR | X86_PF_USER;

arch/x86/lib/insn-eval.c

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1415,6 +1415,25 @@ void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)
14151415
}
14161416
}
14171417

1418+
static unsigned long insn_get_effective_ip(struct pt_regs *regs)
1419+
{
1420+
unsigned long seg_base = 0;
1421+
1422+
/*
1423+
* If not in user-space long mode, a custom code segment could be in
1424+
* use. This is true in protected mode (if the process defined a local
1425+
* descriptor table), or virtual-8086 mode. In most of the cases
1426+
* seg_base will be zero as in USER_CS.
1427+
*/
1428+
if (!user_64bit_mode(regs)) {
1429+
seg_base = insn_get_seg_base(regs, INAT_SEG_REG_CS);
1430+
if (seg_base == -1L)
1431+
return 0;
1432+
}
1433+
1434+
return seg_base + regs->ip;
1435+
}
1436+
14181437
/**
14191438
* insn_fetch_from_user() - Copy instruction bytes from user-space memory
14201439
* @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)
14311450
*/
14321451
int insn_fetch_from_user(struct pt_regs *regs, unsigned char buf[MAX_INSN_SIZE])
14331452
{
1434-
unsigned long seg_base = 0;
1453+
unsigned long ip;
14351454
int not_copied;
14361455

1437-
/*
1438-
* If not in user-space long mode, a custom code segment could be in
1439-
* use. This is true in protected mode (if the process defined a local
1440-
* descriptor table), or virtual-8086 mode. In most of the cases
1441-
* seg_base will be zero as in USER_CS.
1442-
*/
1443-
if (!user_64bit_mode(regs)) {
1444-
seg_base = insn_get_seg_base(regs, INAT_SEG_REG_CS);
1445-
if (seg_base == -1L)
1446-
return 0;
1447-
}
1456+
ip = insn_get_effective_ip(regs);
1457+
if (!ip)
1458+
return 0;
1459+
1460+
not_copied = copy_from_user(buf, (void __user *)ip, MAX_INSN_SIZE);
14481461

1462+
return MAX_INSN_SIZE - not_copied;
1463+
}
1464+
1465+
/**
1466+
* insn_fetch_from_user_inatomic() - Copy instruction bytes from user-space memory
1467+
* while in atomic code
1468+
* @regs: Structure with register values as seen when entering kernel mode
1469+
* @buf: Array to store the fetched instruction
1470+
*
1471+
* Gets the linear address of the instruction and copies the instruction bytes
1472+
* to the buf. This function must be used in atomic context.
1473+
*
1474+
* Returns:
1475+
*
1476+
* Number of instruction bytes copied.
1477+
*
1478+
* 0 if nothing was copied.
1479+
*/
1480+
int insn_fetch_from_user_inatomic(struct pt_regs *regs, unsigned char buf[MAX_INSN_SIZE])
1481+
{
1482+
unsigned long ip;
1483+
int not_copied;
1484+
1485+
ip = insn_get_effective_ip(regs);
1486+
if (!ip)
1487+
return 0;
14491488

1450-
not_copied = copy_from_user(buf, (void __user *)(seg_base + regs->ip),
1451-
MAX_INSN_SIZE);
1489+
not_copied = __copy_from_user_inatomic(buf, (void __user *)ip, MAX_INSN_SIZE);
14521490

14531491
return MAX_INSN_SIZE - not_copied;
14541492
}

0 commit comments

Comments
 (0)