libretro / libretro-super

Super repo for other libretro projects. Fetches, builds and installs.
MIT License
400 stars 279 forks source link

BUG: Libco defect in many libretro-cores causes crashes in Windows x64 #1643

Open snaphat opened 2 years ago

snaphat commented 2 years ago

Many of the libretro cores contain a defect in libco that causes floating point register corruption under x86_64 Windows due to incorrect handwritten 'machine code' found here for example.

Specifically, the issue is that the restore code uses the wrong opcode for MOVAPS when attempting to restore callee-saved registers. The bug appears to originate in the earliest incarnations of libco. It seems to have been fixed some years ago in some of the libretro repositories when an update was pulled from the mainline libco repository (e.g. here). Many of the repositories hold a broken copy though, and it does result in real-world crashes and/or broken behavior.

More specifically, lines 62 through 71 use opcode 0x29 instead of 0x28. Semantically, the difference is as follows:

The former is for storing a memory value into a register, and the latter is for storing register contents into a memory location.

The earliest incarnations of the code were uncommented and written in an era when 64-bit processors were rare, so it isn't surprising it wasn't fixed in mainline for almost a decade. Interestingly, eventually the incorrect code was commented with the actual assembly it represents and even it shows the operations are incorrect (in the libretro copies). The original author of those comments probably didn't realize the implications at the time.

This issue was found while investigating a crash bug in scummvm.

I've determined that the following scummvm games are affected by this issue and either crash or have severe display issues as result of the fault:

The following scummvm games are most likely affected by this issue but I am unable to confirm:

Here is a list of related open issue reports that have a high probability of having been caused by this bug:

The following is a list of ALL cores that currently contain the faulty code in the main branch. I am unable to determine if this causes fault behavior (in practice) on any of the cores outside of scummvm: - 3dengine

The following is a list of additional subrepositories within the libretro organization that currently contain the faulty code in the main branch:

The following is a list of 3rd party libretro repos that currently contain the faulty code in the main branch:

hizzlekizzle commented 2 years ago

well damn, thanks for the excellent bug report!

snaphat commented 2 years ago

Couple of things I neglected to mention are as follows:

1) Fault behavior (in practice) will be dependent on the exact compiler used, version, compilation options, and core code itself. So some games may just work in scummvm on one version of retroarch or core and then break in another, etc. 2) -O2 compilation does not result in fault behavior because the xmm registers are not used to store pointers. This is why the debug build of retroarch does not exhibit the bug (as some of the reports indicate). 3) Declaring local variables as volatile works around the issue by ensuring that they are not saved into xmm registers. 4) Binaries compiled with -03 and -g exhibit the issue. Noting this due to a comment I saw in one of the linked threads regarding debugging this issue on release builds of Retroarch. Issues like these that only show up in 'release builds' can be debugged by removing the stripping, adding -g, and providing an executable to users.

i30817 commented 2 years ago

Does this only affect windows and not linux?

snaphat commented 2 years ago

Does this only affect windows and not linux?

Linux uses the System V ABI which doesn't require callee-saving the XMM6-XMM15 registers so it shouldn't be affected by this issue specifically.

I reviewed the libco call-site code for both Windows 64-bit and System V 64-bit and didn't see any issues outside of this particular XMM issue. All other callee-save registers appear to be properly saved and restored in both the inline assembly and inline machine code for both ABIs.

For reference to the calling conventions, see Table 4 here.