In order to give us more time to prepare a proper patch for the general problem as stated in bug 1800898, I propose a patch that will bypass the specific instance of the problem that we have identified on our Windows 11 21H2 workers, although this will not fix the general problem. The patch implements a tail-call optimization for detoured code. When we recognize that the last instruction we hook is a call, instead of using a `call` instruction that would return to our detoured code, we can simulate a call with the `push` and `jmp` instructions, and push a return address that will return directly to the original code. The problematic hooked function in Windows 11 21H2 workers is as follows: ``` BaseThreadInitThunk 180015540 48 83 ec 28 SUB RSP,0x28 180015544 85 c9 TEST ECX,ECX 180015546 75 17 JNZ LAB_18001555f 180015548 49 8b c8 MOV RCX,R8 18001554b e8 30 ff 00 00 CALL BaseThreadInitXfgThunk 180015550 8b c8 MOV ECX,EAX ``` Without the optimization, the detoured code looks like this on the Windows 11 21H2 workers: ``` 0:006> u poi(mozglue!stub_BaseThreadInitThunk) 000001d4`d84a0010 4883ec28 sub rsp,28h 000001d4`d84a0014 85c9 test ecx,ecx 000001d4`d84a0016 740e je 000001d4`d84a0026 000001d4`d84a0018 ff2500000000 jmp qword ptr [000001d4`d84a001e] 000001d4`d84a001e ... 00007ff8`a99e555f (KERNEL32!BaseThreadInitThunk+0x1f) 000001d4`d84a0026 498bc8 mov rcx,r8 000001d4`d84a0029 ff1502000000 call qword ptr [000001d4`d84a0031] 000001d4`d84a002f eb08 jmp 000001d4`d84a0039 000001d4`d84a0031 ... 00007ff8`a99f5480 (KERNEL32!BaseThreadInitXfgThunk) 000001d4`d84a0039 ff2500000000 jmp qword ptr [000001d4`d84a003f] 000001d4`d84a003f ... 00007ff8`a99e5550 (KERNEL32!BaseThreadInitThunk+0x10) ``` With the optimization, it nows looks like this: ``` 0:006> u poi(mozglue!stub_BaseThreadInitThunk) 00000126`bc7c0010 4883ec28 sub rsp,28h 00000126`bc7c0014 85c9 test ecx,ecx 00000126`bc7c0016 740e je 00000126`bc7c0026 00000126`bc7c0018 ff2500000000 jmp qword ptr [00000126`bc7c001e] 00000126`bc7c001e ... 00007ff8`a99e555f (KERNEL32!BaseThreadInitThunk+0x1f) 00000126`bc7c0026 498bc8 mov rcx,r8 00000126`bc7c0029 ff3506000000 push qword ptr [00000126`bc7c0035] 00000126`bc7c002f ff2508000000 jmp qword ptr [00000126`bc7c003d] 00000126`bc7c0035 ... 00007ff8`a99e5550 (KERNEL32!BaseThreadInitThunk+0x10) 00000126`bc7c003d ... 00007ff8`a99f5480 (KERNEL32!BaseThreadInitXfgThunk) ``` Because the address pushed on the stack is part of the original code for BaseThreadInitThunk, it benefits from the unwinding information embedded in `kernel32.dll`. Callees thus manage to do proper stack unwinding and all structured exception handlers get called.
Bug 1798787 Comment 12 Edit History
Note: The actual edited comment in the bug view page will always show the original commenter’s name and original timestamp.
In order to give us more time to prepare a proper patch for the general problem as stated in bug 1800898, I propose a patch that will bypass the specific instance of the problem that we have identified on our Windows 11 21H2 workers, although this will not fix the general problem. The patch implements a tail-call optimization for detoured code. When we recognize that the last instruction we hook is a call, instead of using a `call` instruction that would return to our detoured code, we can simulate a call with the `push` and `jmp` instructions, and push a return address that will return directly to the original code. The problematic hooked function in Windows 11 21H2 workers is as follows: ``` BaseThreadInitThunk 180015540 48 83 ec 28 SUB RSP,0x28 180015544 85 c9 TEST ECX,ECX 180015546 75 17 JNZ LAB_18001555f 180015548 49 8b c8 MOV RCX,R8 18001554b e8 30 ff 00 00 CALL BaseThreadInitXfgThunk 180015550 8b c8 MOV ECX,EAX ``` Without the optimization, the detoured code looks like this on the Windows 11 21H2 workers: ``` 0:006> u poi(mozglue!stub_BaseThreadInitThunk) 000001d4`d84a0010 4883ec28 sub rsp,28h 000001d4`d84a0014 85c9 test ecx,ecx 000001d4`d84a0016 740e je 000001d4`d84a0026 000001d4`d84a0018 ff2500000000 jmp qword ptr [000001d4`d84a001e] 000001d4`d84a001e ... 00007ff8`a99e555f (KERNEL32!BaseThreadInitThunk+0x1f) 000001d4`d84a0026 498bc8 mov rcx,r8 000001d4`d84a0029 ff1502000000 call qword ptr [000001d4`d84a0031] 000001d4`d84a002f eb08 jmp 000001d4`d84a0039 000001d4`d84a0031 ... 00007ff8`a99f5480 (KERNEL32!BaseThreadInitXfgThunk) 000001d4`d84a0039 ff2500000000 jmp qword ptr [000001d4`d84a003f] 000001d4`d84a003f ... 00007ff8`a99e5550 (KERNEL32!BaseThreadInitThunk+0x10) ``` With the optimization, it now looks like this: ``` 0:006> u poi(mozglue!stub_BaseThreadInitThunk) 00000126`bc7c0010 4883ec28 sub rsp,28h 00000126`bc7c0014 85c9 test ecx,ecx 00000126`bc7c0016 740e je 00000126`bc7c0026 00000126`bc7c0018 ff2500000000 jmp qword ptr [00000126`bc7c001e] 00000126`bc7c001e ... 00007ff8`a99e555f (KERNEL32!BaseThreadInitThunk+0x1f) 00000126`bc7c0026 498bc8 mov rcx,r8 00000126`bc7c0029 ff3506000000 push qword ptr [00000126`bc7c0035] 00000126`bc7c002f ff2508000000 jmp qword ptr [00000126`bc7c003d] 00000126`bc7c0035 ... 00007ff8`a99e5550 (KERNEL32!BaseThreadInitThunk+0x10) 00000126`bc7c003d ... 00007ff8`a99f5480 (KERNEL32!BaseThreadInitXfgThunk) ``` Because the address pushed on the stack is part of the original code for BaseThreadInitThunk, it benefits from the unwinding information embedded in `kernel32.dll`. Callees thus manage to do proper stack unwinding and all structured exception handlers get called.
In order to give us more time to prepare a proper patch for the general problem as stated in bug 1800898, I propose a patch that will bypass the specific instance of the problem that we have identified on our Windows 11 21H2 workers, although this will not fix the general problem. The patch implements a tail-call optimization for detoured code. When we recognize that the last instruction we hook is a call, instead of using a `call` instruction that would return to our detoured code, we can simulate a call with the `push` and `jmp` instructions, and push a return address that will return directly to the original code. The problematic hooked function in Windows 11 21H2 workers is as follows: ``` BaseThreadInitThunk 180015540 48 83 ec 28 SUB RSP,0x28 180015544 85 c9 TEST ECX,ECX 180015546 75 17 JNZ LAB_18001555f 180015548 49 8b c8 MOV RCX,R8 18001554b e8 30 ff 00 00 CALL BaseThreadInitXfgThunk 180015550 8b c8 MOV ECX,EAX ``` Without the optimization, the detoured code looks like this on the Windows 11 21H2 workers: ``` 0:006> u poi(mozglue!stub_BaseThreadInitThunk) 000001d4`d84a0010 4883ec28 sub rsp,28h 000001d4`d84a0014 85c9 test ecx,ecx 000001d4`d84a0016 740e je 000001d4`d84a0026 000001d4`d84a0018 ff2500000000 jmp qword ptr [000001d4`d84a001e] 000001d4`d84a001e ... 00007ff8`a99e555f (KERNEL32!BaseThreadInitThunk+0x1f) 000001d4`d84a0026 498bc8 mov rcx,r8 000001d4`d84a0029 ff1502000000 call qword ptr [000001d4`d84a0031] 000001d4`d84a002f eb08 jmp 000001d4`d84a0039 000001d4`d84a0031 ... 00007ff8`a99f5480 (KERNEL32!BaseThreadInitXfgThunk) 000001d4`d84a0039 ff2500000000 jmp qword ptr [000001d4`d84a003f] 000001d4`d84a003f ... 00007ff8`a99e5550 (KERNEL32!BaseThreadInitThunk+0x10) ``` With the optimization, it now looks like this: ``` 0:006> u poi(mozglue!stub_BaseThreadInitThunk) 00000126`bc7c0010 4883ec28 sub rsp,28h 00000126`bc7c0014 85c9 test ecx,ecx 00000126`bc7c0016 740e je 00000126`bc7c0026 00000126`bc7c0018 ff2500000000 jmp qword ptr [00000126`bc7c001e] 00000126`bc7c001e ... 00007ff8`a99e555f (KERNEL32!BaseThreadInitThunk+0x1f) 00000126`bc7c0026 498bc8 mov rcx,r8 00000126`bc7c0029 ff3506000000 push qword ptr [00000126`bc7c0035] 00000126`bc7c002f ff2508000000 jmp qword ptr [00000126`bc7c003d] 00000126`bc7c0035 ... 00007ff8`a99e5550 (KERNEL32!BaseThreadInitThunk+0x10) 00000126`bc7c003d ... 00007ff8`a99f5480 (KERNEL32!BaseThreadInitXfgThunk) ``` Because the address pushed on the stack is part of the original code for `BaseThreadInitThunk`, it benefits from the unwinding information embedded in `kernel32.dll`. Callees thus manage to do proper stack unwinding and all structured exception handlers get called.