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 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.
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.

Back to Bug 1798787 Comment 12