In bug 47198 (https://bugs.winehq.org/show_bug.cgi?id=47198), we found that League of Legends is trying to access the 64-bit TEB if it detects that it is running under a 64-bit kernel. It does this by checking if the %cs segment selector matches a WoW64 environment, and if it does, it will then access %gs:0x60.
On WoW64, %gs points to the 64-bit TEB and %gs:0x60 is a pointer to the 64-bit PEB. Under Wine on Linux, however, %gs is reserved for glibc and %gs:0x60 contains a list pointer that we cannot corrupt without crashing. The current solution in the referenced bug is to patch glibc to reserve some space for Wine; thankfully that portion of the TEB is glibc internal and can be reserved without rebuilding other libraries.
So, my question is how to move forward. I see a few options:
1. Require users to have a patched glibc. 1a) Ask glibc nicely to upstream this patch. 1b) Lutris and downstream packagers can ship a compatible glibc with their Wine builds if they choose. 2. Similar to the aarch64 issue with x18, find a way to save and restore %gs at the boundary between PE modules and native modules. 3. Find some way to trap accesses to %gs:0x60 without using hardware breakpoints and without destroying performance.
As an aside, I realize that it is impossible for Wine to perfectly emulate a Windows environment and at some point it may be impossible to continue to support apps that choose to apply aggressive anti-debugging / obfuscation.
On May 19, 2019, at 3:54 PM, Andrew Wesie awesie@gmail.com wrote:
In bug 47198 (https://bugs.winehq.org/show_bug.cgi?id=47198), we found that League of Legends is trying to access the 64-bit TEB if it detects that it is running under a 64-bit kernel. It does this by checking if the %cs segment selector matches a WoW64 environment, and if it does, it will then access %gs:0x60.
I take it the program is a 32-bit program. Otherwise, this would be fine.
On WoW64, %gs points to the 64-bit TEB and %gs:0x60 is a pointer to the 64-bit PEB. Under Wine on Linux, however, %gs is reserved for glibc and %gs:0x60 contains a list pointer that we cannot corrupt without crashing. The current solution in the referenced bug is to patch glibc to reserve some space for Wine; thankfully that portion of the TEB is glibc internal and can be reserved without rebuilding other libraries.
So, my question is how to move forward. I see a few options:
- Require users to have a patched glibc.
1a) Ask glibc nicely to upstream this patch. 1b) Lutris and downstream packagers can ship a compatible glibc with their Wine builds if they choose. 2. Similar to the aarch64 issue with x18, find a way to save and restore %gs at the boundary between PE modules and native modules. 3. Find some way to trap accesses to %gs:0x60 without using hardware breakpoints and without destroying performance.
Perhaps 4: use a different code segment so that the %cs segment selector doesn't look like a WoW64 environment. Kind of specific to this particular app's behavior, so not great, but maybe passable.
-Ken
On Sun, May 19, 2019 at 4:07 PM Ken Thomases ken@codeweavers.com wrote:
Perhaps 4: use a different code segment so that the %cs segment selector doesn't look like a WoW64 environment. Kind of specific to this particular app's behavior, so not great, but maybe passable.
This possibility was considered but I didn't think it was possible to do this on Linux any more without patching the kernel. Notably from the man page for set_thread_area: "Since Linux 3.19, set_thread_area() cannot be used to write non-present segments, 16-bit segments, or code segments, although clearing a segment is still acceptable." The associated source code: https://elixir.bootlin.com/linux/v3.19/source/arch/x86/kernel/tls.c#L63.
Please let me know if my understanding is incorrect.
On 5/19/19 4:58 PM, Andrew Wesie wrote:
On Sun, May 19, 2019 at 4:07 PM Ken Thomases ken@codeweavers.com wrote:
Perhaps 4: use a different code segment so that the %cs segment selector doesn't look like a WoW64 environment. Kind of specific to this particular app's behavior, so not great, but maybe passable.
This possibility was considered but I didn't think it was possible to do this on Linux any more without patching the kernel. Notably from the man page for set_thread_area: "Since Linux 3.19, set_thread_area() cannot be used to write non-present segments, 16-bit segments, or code segments, although clearing a segment is still acceptable." The associated source code: https://elixir.bootlin.com/linux/v3.19/source/arch/x86/kernel/tls.c#L63.
Please let me know if my understanding is incorrect.
Presumably we could use the LDT [and modify_ldt(2)] instead, as we already do for NE segmentation?
Ah, thanks. It didn't occur to me that set_thread_area and modify_ldt would have different behavior w.r.t. allowing code segments.
I'll play around with it and see what happens.
On Sun, May 19, 2019 at 5:42 PM Zebediah Figura z.figura12@gmail.com wrote:
On 5/19/19 4:58 PM, Andrew Wesie wrote:
On Sun, May 19, 2019 at 4:07 PM Ken Thomases ken@codeweavers.com wrote:
Perhaps 4: use a different code segment so that the %cs segment selector doesn't look like a WoW64 environment. Kind of specific to this particular app's behavior, so not great, but maybe passable.
This possibility was considered but I didn't think it was possible to do this on Linux any more without patching the kernel. Notably from the man page for set_thread_area: "Since Linux 3.19, set_thread_area() cannot be used to write non-present segments, 16-bit segments, or code segments, although clearing a segment is still acceptable." The associated source code: https://elixir.bootlin.com/linux/v3.19/source/arch/x86/kernel/tls.c#L63.
Please let me know if my understanding is incorrect.
Presumably we could use the LDT [and modify_ldt(2)] instead, as we already do for NE segmentation?
- Similar to the aarch64 issue with x18, find a way to save and
restore %gs at the boundary between PE modules and native modules.
IMHO #2 sounds like the best possible course. We already have something similar for user32 wndprocs, although on a way less intrusive scale.
Here the x18 staging patch for reference: https://github.com/wine-staging/ wine-staging/blob/00b434ec7f1123c221dae2006b33339a808cb24b/patches/ntdll- aarch-TEB/0002-ntdll-Always-restore-TEB-to-x18-on-aarch-64-on-retur.patch
Would something like this already work? Ideally we wouldn't want to use relay for that, but it would be a start. If this covers the usecases of that game, that is.
On a side-note, does anyone know how big the performance hit would be? Because in my naive thinking, it shouldn't be too bad. But then again, I'm not familiar with performance tuning.
Regards, Fabian Maurer