Bug 400538

Summary: vex amd64->IR: unhandled instruction bytes: 0x48 0xCF 0xF 0x1F 0x0 0xFF 0xD2 0xCC 0x90 0x55
Product: [Developer tools] valgrind Reporter: Alex Henrie <alexhenrie24>
Component: vexAssignee: Julian Seward <jseward>
Status: RESOLVED FIXED    
Severity: normal CC: austinenglish, dlehman25, dougvj, eeknaak
Priority: NOR    
Version: 3.14 SVN   
Target Milestone: ---   
Platform: Arch Linux   
OS: Linux   
See Also: https://bugs.kde.org/show_bug.cgi?id=253657
Latest Commit: Version Fixed In:
Sentry Crash Report:
Attachments: IRETQ Test Case
iretq implementation
updated iretq implementation

Description Alex Henrie 2018-11-01 02:31:14 UTC
When I try to use Valgrind to debug any 64-bit Windows program, I get the following error:

vex amd64->IR: unhandled instruction bytes: 0x48 0xCF 0xF 0x1F 0x0 0xFF 0xD2 0xCC 0x90 0x55
vex amd64->IR:   REX=1 REX.W=1 REX.R=0 REX.X=0 REX.B=0
vex amd64->IR:   VEX=0 VEX.L=0 VEX.nVVVV=0x0 ESC=NONE
vex amd64->IR:   PFX.66=0 PFX.F2=0 PFX.F3=0
vex amd64->IR: unhandled instruction bytes: 0x48 0xCF 0xF 0x1F 0x0 0xFF 0xD2 0xCC 0x90 0x55
vex amd64->IR:   REX=1 REX.W=1 REX.R=0 REX.X=0 REX.B=0
vex amd64->IR:   VEX=0 VEX.L=0 VEX.nVVVV=0x0 ESC=NONE
vex amd64->IR:   PFX.66=0 PFX.F2=0 PFX.F3=0

This is easily reproducible by running `valgrind --trace-children=yes wine notepad`.

For what it's worth, I run Arch Linux and all packages are up-to-date.
Comment 1 Julian Seward 2019-03-13 15:31:38 UTC
0x48 0xCF is IRETQ (return from interrupt) and it segfaults when run
even natively (not on V) on my Fedora 29 box.  So I'm kinda surprised
that you expect it to work when running on V.  But maybe I misunderstand
what's going on here?

My test case is

int main ( void )
{
   __asm__ __volatile__(".byte 0x48, 0xCF" ::: "cc","memory");
   return 0;
}
Comment 2 Doug Johnson 2019-03-13 20:43:03 UTC
IRETQ appears to be used by wine to start executing a CPU context. In normal operation this context is generated by the CPU when it is interrupted and pushed onto the stack, which is picked up by IRETQ when the interrupt is done being handled. Wine appears to generate this context on the stack itself so it's not using one generated by the CPU for IRETQ. 

Simply executing IRETQ without a valid CPU context on the stack will surely cause a segfault as the stack doesn't contain a valid instruction pointer and other CPU state. The segfault may even be caused by a stack underflow in this case, I am not sure.
Comment 3 Doug Johnson 2019-03-13 20:45:58 UTC
See the wine source here for usage of IRETQ:
https://github.com/wine-mirror/wine/blob/master/dlls/ntdll/signal_x86_64.c
Comment 4 Alex Henrie 2019-03-14 04:22:35 UTC
Wine has been using IRETQ since May 2009: https://source.winehq.org/git/wine.git/commitdiff/880d00fb43de924a3543b0ad53b5aaf0ad63d0cb

The first reference I could find to IRETQ in Wine causing a problem with Valgrind was in January 2013: https://sourceforge.net/p/valgrind/mailman/message/30422124/
Comment 5 Doug Johnson 2019-03-16 08:38:59 UTC
Created attachment 118834 [details]
IRETQ Test Case

I went ahead and implemented a minimal test case which is attached. I confirmed the code runs fine on metal but chokes in valgrind producing a similar error message: unhandled instruction bytes: 0x48 0xCF 0xDE 0xAD 0xBE 0xEF
Comment 6 Doug Johnson 2019-03-16 08:54:48 UTC
Comment on attachment 118834 [details]
IRETQ Test Case

>#include <stdio.h>
>#include <stdlib.h>
>
>void return_from_iret() {
>    printf("Hello From IRETQ\n");
>    exit(0);
>}
>
>
>int main() {
>    asm (
>        "pushfq\n"
>        "movq 0(%%rsp), %%rbx\n" //rbx contains eflags
>        "popfq\n"
>        "subq $40,   %%rsp\n" //Allocate our stack area for iret
>        "movq %%ss,  %%rax\n"
>        "movq %%rax, 32(%%rsp)\n"   //SS
>        "movq %%rsp,  %%rax\n"
>        "movq %%rax, 24(%%rsp)\n" //SP
>        "movq %%rbx, 16(%%rsp)\n" //EFLAGS
>        "movq %%cs,  %%rax\n"
>        "movq %%rax, 8(%%rsp)\n"  //CS
>        "movq %0,    0(%%rsp)\n"  //RIP
>        "iretq\n"
>        ".byte 0xDE, 0xAD, 0xBE, 0xEF\n"
>        : //no outputs
>        : "r" (return_from_iret)
>        : "rax", "rbx" //clobber list
>    );
>    return 1;
>}
Comment 7 Austin English 2019-04-11 03:24:44 UTC
Okay, I can reproduce this, it needs a couple more valgrind arguments.

# First, start a wine process (so that wineserver is running before valgrind starts):
$ wine64 start /min winemine

# Then, start notepad under valgrind:
$ austin@laptop:~/src/valgrind$ valgrind --trace-children=yes --vex-iropt-register-updates=allregs-at-mem-access /opt/oldwow64/wine-4.5/bin/wine64 notepad

==2874== Memcheck, a memory error detector
==2874== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2874== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==2874== Command: /opt/oldwow64/wine-4.5/bin/wine64 notepad
==2874== 
==2874== Memcheck, a memory error detector
==2874== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2874== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==2874== Command: /opt/oldwow64/wine-4.5/bin/wine64-preloader /opt/oldwow64/wine-4.5/bin/wine64 notepad
==2874== 
preloader: Warning: failed to reserve range 0000000000110000-0000000068000000
==2874== 
vex amd64->IR: unhandled instruction bytes: 0x48 0xCF 0xF 0x1F 0x0 0xFF 0xD2 0xCC 0x90 0x55
vex amd64->IR:   REX=1 REX.W=1 REX.R=0 REX.X=0 REX.B=0
vex amd64->IR:   VEX=0 VEX.L=0 VEX.nVVVV=0x0 ESC=NONE
vex amd64->IR:   PFX.66=0 PFX.F2=0 PFX.F3=0
vex amd64->IR: unhandled instruction bytes: 0x48 0xCF 0xF 0x1F 0x0 0xFF 0xD2 0xCC 0x90 0x55
vex amd64->IR:   REX=1 REX.W=1 REX.R=0 REX.X=0 REX.B=0
vex amd64->IR:   VEX=0 VEX.L=0 VEX.nVVVV=0x0 ESC=NONE
vex amd64->IR:   PFX.66=0 PFX.F2=0 PFX.F3=0
==2874== valgrind: Unrecognised instruction at address 0x7bc9bff3.
==2874==    at 0x7BC9BFF3: ??? (in /opt/oldwow64/wine-4.5/lib64/wine/ntdll.dll.so)
==2874==    by 0x7BC9C0CA: ??? (in /opt/oldwow64/wine-4.5/lib64/wine/ntdll.dll.so)
==2874== Your program just tried to execute an instruction that Valgrind
==2874== did not recognise.  There are two possible reasons for this.
==2874== 1. Your program has a bug and erroneously jumped to a non-code
==2874==    location.  If you are running Memcheck and you just saw a
==2874==    warning about a bad jump, it's probably your program's fault.
==2874== 2. The instruction is legitimate but Valgrind doesn't handle it,
==2874==    i.e. it's Valgrind's fault.  If you think this is the case or
==2874==    you are not sure, please let us know and we'll try to fix it.
==2874== Either way, Valgrind will now raise a SIGILL signal which will
==2874== probably kill your program.
005d:err:seh:segv_handler Got unexpected trap 0
==2874== Invalid write of size 8
==2874==    at 0x7BC9BFF8: ??? (in /opt/oldwow64/wine-4.5/lib64/wine/ntdll.dll.so)
==2874==    by 0x7BC9BFF2: ??? (in /opt/oldwow64/wine-4.5/lib64/wine/ntdll.dll.so)
==2874==    by 0x7BC9C0CA: ??? (in /opt/oldwow64/wine-4.5/lib64/wine/ntdll.dll.so)
==2874==  Address 0x7ffffe20f4b8 is in a rw- anonymous segment
==2874== 
005d:err:seh:NtRaiseException Unhandled exception code c000001d flags 0 addr 0x7bc9bff3
==2874== 
==2874== HEAP SUMMARY:
==2874==     in use at exit: 731,905 bytes in 6,501 blocks
==2874==   total heap usage: 13,837 allocs, 7,336 frees, 2,963,926 bytes allocated
==2874== 
==2874== LEAK SUMMARY:
==2874==    definitely lost: 318 bytes in 2 blocks
==2874==    indirectly lost: 0 bytes in 0 blocks
==2874==      possibly lost: 0 bytes in 0 blocks
==2874==    still reachable: 731,587 bytes in 6,499 blocks
==2874==         suppressed: 0 bytes in 0 blocks
==2874== Rerun with --leak-check=full to see details of leaked memory
==2874== 
==2874== For counts of detected and suppressed errors, rerun with: -v
==2874== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

Tested with 3.15-rc1 / wine-4.5
Comment 8 Daniel Lehman 2019-05-01 05:43:03 UTC
Created attachment 119759 [details]
iretq implementation

updated version of the iretq implementation i included in the tarball in https://bugs.kde.org/show_bug.cgi?id=253657
Comment 9 Julian Seward 2019-07-10 14:54:18 UTC
(In reply to Daniel Lehman from comment #8)
> Created attachment 119759 [details]
> iretq implementation
> 
> updated version of the iretq implementation i included in the tarball in
> https://bugs.kde.org/show_bug.cgi?id=253657

Daniel, all, sorry to have been so slow looking at this.  Thank you
for the patch.

The patch ignores the new values for %CS and %SS, which seems reasonable
to me, given that Vex doesn't model segment registers on x86_64 anyway
(per comment above dis_mov_S_E() in guest_amd64_toIR.c).

However, afaics, the patch also ignores the new value for %rflags.d, which
iirc is the string-operation direction flag.  We do need to restore that in
order that any pending string operations continue in the right direction,
I think.  See the implementation for POPF in that same file, for how to
set it.  The Intel docs for IRETQ have this in a couple of places:

  RETURN-TO-SAME-PRIVILEGE-LEVEL: (* PE = 1, RPL = CPL *)
    ...
    EFLAGS (CF, PF, AF, ZF, SF, TF, DF, OF, NT) ← tempEFLAGS;

which is why I think at least D should be restored.  I notice that
Vex also models the ID and AC flags (see, again, the POPF implementation)
but from the Intel docs it's not clear to me whether these also need
to be restoired from 'tempEFLAGS' above.
Comment 10 Daniel Lehman 2019-07-15 02:34:50 UTC
Created attachment 121516 [details]
updated iretq implementation

> Vex also models the ID and AC flags (see, again, the POPF implementation)
> but from the Intel docs it's not clear to me whether these also need
> to be restoired from 'tempEFLAGS' above
from the docs, it does look like they need to be restored.  using the attached
iretq test case in a debugger, i can see that the ID and AC are saved across iretq

> EFLAGS (CF, PF, AF, ZF, SF, TF, DF, OF, NT) ← tempEFLAGS;
it doesn't look like VEX models certain flags like NT or TF

> See the implementation for POPF in that same file, for how to set it
the attached uses the code from popf.  i retested with the latest wine 4.12.1
Comment 11 Julian Seward 2019-08-19 14:09:58 UTC
Committed, e7dde4bc20bf57b4c1e25801f8462d1519c4fa41.  Thanks for the patch.

I took the liberty of adding an extra condition which disallows decoding
if there is an 0x66, 0xF2 or 0xF3 prefix present, which I think should make
it a little safer.
Comment 12 Alex Henrie 2019-08-19 17:08:03 UTC
Thank you Julian, having this patch committed is a big help to the Wine project!
Comment 13 Tom Hughes 2019-11-29 18:59:27 UTC
*** Bug 414659 has been marked as a duplicate of this bug. ***