From 4c5d7a141f266f58f2292f8f448b01df18957002 Mon Sep 17 00:00:00 2001 From: trasz Date: Sun, 18 Oct 2020 16:16:22 +0000 Subject: [PATCH] Stop calling set_syscall_retval() from linux_set_syscall_retval(). The former clobbers some registers that shouldn't be touched. Reviewed by: kib (earlier version) MFC after: 2 weeks Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D26406 --- sys/amd64/linux/linux_sysvec.c | 47 ++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/sys/amd64/linux/linux_sysvec.c b/sys/amd64/linux/linux_sysvec.c index 3c578b36eac..98dc6bcbd10 100644 --- a/sys/amd64/linux/linux_sysvec.c +++ b/sys/amd64/linux/linux_sysvec.c @@ -206,24 +206,45 @@ linux_fetch_syscall_args(struct thread *td) static void linux_set_syscall_retval(struct thread *td, int error) { - struct trapframe *frame = td->td_frame; + struct trapframe *frame; - /* - * On Linux only %rcx and %r11 values are not preserved across - * the syscall. So, do not clobber %rdx and %r10. - */ - td->td_retval[1] = frame->tf_rdx; - if (error != EJUSTRETURN) - frame->tf_r10 = frame->tf_rcx; + frame = td->td_frame; - cpu_set_syscall_retval(td, error); + switch (error) { + case 0: + frame->tf_rax = td->td_retval[0]; + frame->tf_r10 = frame->tf_rcx; + break; - if (__predict_false(error != 0)) { - if (error != ERESTART && error != EJUSTRETURN) - frame->tf_rax = linux_to_bsd_errno(error); + case ERESTART: + /* + * Reconstruct pc, we know that 'syscall' is 2 bytes, + * lcall $X,y is 7 bytes, int 0x80 is 2 bytes. + * We saved this in tf_err. + * + */ + frame->tf_rip -= frame->tf_err; + frame->tf_r10 = frame->tf_rcx; + break; + + case EJUSTRETURN: + break; + + default: + frame->tf_rax = linux_to_bsd_errno(error); + frame->tf_r10 = frame->tf_rcx; + break; } - /* Restore all registers. */ + /* + * Differently from FreeBSD native ABI, on Linux only %rcx + * and %r11 values are not preserved across the syscall. + * Require full context restore to get all registers except + * those two restored at return to usermode. + * + * XXX: Would be great to be able to avoid PCB_FULL_IRET + * for the error == 0 case. + */ set_pcb_flags(td->td_pcb, PCB_FULL_IRET); } -- 2.45.0