On Mar  1 22:43, Johannes Schindelin wrote:
> Reportedly a very recent internal build of Windows 11 once again changed
> the current working directory logic a bit, and Cygwin's "magic" (or:
> "technologically sufficiently advanced") code needs to be adjusted

*cough, cough*

> accordingly.
> 
> In particular, the following assembly code can be seen:
> 
> ntdll!RtlpReferenceCurrentDirectory
> 
>   598 00000001`800c6925 488d0db4cd0f00  lea     rcx,[ntdll!FastPebLock 
> (00000001`801c36e0)]
>   583 00000001`800c692c 4c897810        mov     qword ptr [rax+10h],r15
>   588 00000001`800c6930 0f1140c8        movups  xmmword ptr [rax-38h],xmm0
>   598 00000001`800c6934 e82774f4ff      call    ntdll!RtlEnterCriticalSection
> 
> The change necessarily looks a bit different than 4840a56325 (Cygwin:
> Adjust CWD magic to accommodate for the latest Windows previews,
> 2023-05-22): The needle `\x48\x8d\x0d` is already present, as the first
> version of the hack after Windows 8.1 was released. In that code,
> though, the `call` to `RtlEnterCriticalSection` followed the `lea`
> instruction immediately, but now there are two more instructions
> separating them.
> 
> Note: In the long run, we may very well want to follow the insightful
> suggestion by a helpful Windows kernel engineer who pointed out that it
> may be less fragile to implement kind of a disassembler that has a
> better chance to adapt to the ever-changing code of
> `ntdll!RtlpReferenceCurrentDirectory` by skipping uninteresting
> instructions such as `mov %rsp,%rax`, `mov %rbx,0x20(%rax)`, `push %rsi`
> `sub $0x70,%rsp`, etc, and focuses on finding the `lea`, `call
> ntdll!RtlEnterCriticalSection` and `mov ..., rbx` instructions, much
> like it was prototyped out for ARM64 at
> https://gist.github.com/jeremyd2019/aa167df0a0ae422fa6ebaea5b60c80c9

I'm always open to patches to improve this code.  But x86 assembler is
really tricky, me thinks, using any number of bytes for an instruction.
It needs a lot of knowledge of instructons and their respective length,
to skip the uninteresting parts.

But I have a few more ideas how to handle this in future:

- Export some Rtl function from ntdll.dll, returning the cwd pointer
  we're looking for,

- or move the pointer into the PEB,

- or export a new function RtlSetCurrentDirectoryEx_U:

    NTSTATUS NTAPI
    RtlSetCurrentDirectoryEx_U (IN PUNICODE_STRING Path,
                                IN POBJECT_ATTRIBUTES ObjectAttributes,
                                IN ULONG ShareAccess,
                                IN ULONG OpenOptions);
                                                
Doesn't this sound like the cleanest way forward?

> diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc
> index 599809f941..49740ac465 100644
> --- a/winsup/cygwin/path.cc
> +++ b/winsup/cygwin/path.cc
> @@ -4539,6 +4539,18 @@ find_fast_cwd_pointer ()
>           %rcx for the subsequent RtlEnterCriticalSection call. */
>        lock = (const uint8_t *) memmem ((const char *) use_cwd, 80,
>                                         "\x48\x8d\x0d", 3);
> +      if (lock)
> +     {
> +       /* A recent Windows 11 Preview calls `lea rel(rip),%rcx' then
> +          a `mov` and a `movups` instruction, and only then
> +          `callq RtlEnterCriticalSection'.
> +          */
> +       if (memmem (lock + 7, 8, "\x4c\x89\x78\x10\x0f\x11\x40\xc8", 8))

Is it really necessary to check for each and every byte between lea and
callq?  I wonder if this can't be simpler by simply checking for the
'\x48\x8d\x0d` needle and then, instead of just assuming a fixed
call_rtl_offset, skip programatically to the next callq 0xe8 byte
within the next 16 bytes or so?


Thanks,
Corinna

Reply via email to