{"id":111738,"date":"2025-10-29T07:00:00","date_gmt":"2025-10-29T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=111738"},"modified":"2025-10-29T09:52:39","modified_gmt":"2025-10-29T16:52:39","slug":"20251029-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20251029-00\/?p=111738","title":{"rendered":"What to do when you have a crash in the runtime control flow guard check"},"content":{"rendered":"<p>Windows Control Flow Guard (CFG) is a defense in depth feature which validates indirect call targets. The idea is that each module that is enabled for CFG provides a bitmap that describes which addresses in the module are intended to be targets of indirect calls. When CFG is enabled in a process, indirect function calls are checked against this table, and if the address is deemed invalid, the process terminates itself, and the Watson service records the details for future investigation.<\/p>\n<p>If you are studying a crash in the control flow guard validator\u00b9 you may want to pick out the failed address so you can understand better what went wrong and use it to guide the next step of your debugging. (Was it a bad address? Was the DLL unloaded? Was it a garbage value due to use-after-free?)<\/p>\n<p>In general, the control flow guard validator takes a function address in some register, performs shifting and masking operations using that register as a source (to calculate the bit position in the call target bitmap), and then tests a bit. The source register is left unchanged so that the caller, on success, can use the validated address as a jump target.<\/p>\n<p>Let&#8217;s practice. Here&#8217;s one of the control flow guard validator functions for x86-64, which Windows often calls x64. Try to spot the register that holds the address being validated.<\/p>\n<pre>ntdll!LdrpValidateUserCallTarget:\r\n    mov     rdx,qword ptr [ntdll!................]\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">mov     rax,rcx                  <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">shr     rax,9                    <\/span> ; shift\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">mov     rdx,qword ptr [rdx+rax*8]<\/span> ; crash here\r\n    mov     rax,rcx\r\n    shr     rax,3\r\n    test    cl,0Fh\r\n    jne     @1\r\n    bt      rdx,rax\r\n    jae     @2\r\n    ret\r\n@1: btr     rax,0\r\n    bt      rdx,rax\r\n    jae     @3\r\n@2: or      rax,1\r\n    bt      rdx,rax\r\n    jae     @3\r\n    ret\r\n@3: mov     rax,rcx\r\n    xor     r10d,r10d\r\n    jmp     ntdll!LdrpHandleInvalidUserCallTarget\r\n<\/pre>\n<p>We see that the value in <code>rcx<\/code> gets moved into <code>rax<\/code>, and then <code>rax<\/code> gets shifted. So the address being validated is in <code>rcx<\/code>. The marked instruction is the only one that accesses memory, so if there&#8217;s a crash, it&#8217;ll happen there. The rest of the function is just bit twiddling.<\/p>\n<p>Let&#8217;s do the same exercise for x86-32, which Windows often just calls x86.<\/p>\n<pre>ntdll!LdrpValidateUserCallTarget:\r\n    mov     edx,dword ptr [ntdll!........]\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">mov     eax,ecx                  <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">shr     eax,8                    <\/span> ; shift\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">mov     edx,dword ptr [edx+eax*4]<\/span> ; crash here\r\n    mov     eax,ecx\r\n    shr     eax,3\r\n    test    cl,0Fh\r\n    jne     @1\r\n    bt      edx,eax\r\n    jae     ...\r\n    ret\r\n@1: btr     eax,0\r\n    bt      edx,eax\r\n    jae     ...\r\n    or      eax,1\r\n    bt      edx,eax\r\n    jae     ...\r\n    ret\r\n<\/pre>\n<p>This time, it&#8217;s the value in <code>ecx<\/code> that gets moved into <code>eax<\/code>, and then <code>eax<\/code> gets shifted. The address being validated is therefore in <code>ecx<\/code>. Again, the marked instruction is the only one that accesses memory.<\/p>\n<p>One more: This time, it&#8217;s 32-bit ARM, which Windows calls simply arm.<\/p>\n<pre>ntdll!LdrpValidateUserCallTarget:\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">mov         r3,#0x.... <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">movt        r3,#0x.... <\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">ldr         r3,[r3]    <\/span>\r\n\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">lsrs        r2,r0,#6   <\/span> ; shift\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">ubfx        r1,r0,#3,#3<\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">ldrb        r2,[r3,r2] <\/span> ; crash here\r\n\r\n    mov         r3,r0\r\n    and         r0,r0,#0xF\r\n    subs        r0,r0,#1\r\n    bne         ...\r\n<\/pre>\n<p>There are two memory accesses this time. The first is loading from a fixed address (built into <code>r3<\/code> in two instructions), so it matches the first instruction of the x86-32 and x86-64 versions; it&#8217;s just that x86 can load from many fixed adresses in just one instruction.<\/p>\n<p>The second group of instructions is the interesting one. It shifts the value in <code>r0<\/code> and puts the result in <code>r2<\/code>. It also uses <code>r0<\/code> as the source for a bit extraction operation that puts the result in <code>r1<\/code>, and then it accesses some memory. So it looks like <code>r0<\/code> is the address, since it&#8217;s the source of the shift instruction.<\/p>\n<p>Mind you, this code modifies <code>r0<\/code> later on, so the value in <code>r0<\/code> doesn&#8217;t hold the address through the entire function. It got copied into <code>r3<\/code> for safekeeping, so if you break in later in the function, you&#8217;ll want to look to <code>r3<\/code> for the address. But if you crash on the memory access, the address is in <code>r0<\/code>.<\/p>\n<p>Our last example is AArch64, which Windows usually calls arm64.<\/p>\n<pre>ntdll!LdrpValidateUserCallTarget:\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">adrp        xip0,ntdll!....   <\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">ldr         xip0,[xip0,#0x598]<\/span>\r\n\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">lsr         xip1,x15,#6     <\/span> ; shift\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">tst         x15,#0xF        <\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">ldrb        wip1,[xip0,xip1]<\/span> ; crash here\r\n    ubfx        xip0,x15,#3,#3\r\n    bne         @2\r\n\r\n    lsr         xip1,xip1,xip0\r\n    tbz         wip1,#0,@3\r\n@1: ret\r\n\r\n@2: and         xip0,xip0,#-2\r\n    lsr         xip1,xip1,xip0\r\n    tbz         wip1,#0,@4\r\n@3: tbnz        wip1,#1,@1\r\n@4: mov         xip0,#0\r\n    b           @5\r\n@5: b           ntdll!LdrpHandleInvalidUserCallTarget\r\n<\/pre>\n<p>Again, we start by loading an address from memory, and then we shift a register, this time the <code>x15<\/code> register. There is a bit test instruction whose result is used later, and then we perform a memory access (which could crash). From inspection, we therefore see that the address being validated is in <code>x15<\/code>.<\/p>\n<p>The point of this exercise is not to memorize the registers that each architecture uses for control flow guard,\u00b3 but rather to take a little information about the design of control flow guard (checking a bit in a bitmap, using the address passed in a register to calculate the index),\u00b2 and using that to figure out on the fly which register you need to look at based on the code surrounding the crashing access.<\/p>\n<p>\u00b9 Usually, these crashes occur because the address that got passed in is so invalid that there is no memory at the location where the bit in the validation bitmap is supposed to be, resulting in an access violation.<\/p>\n<p>\u00b2 You don&#8217;t even have to know the precise meaning of the bits in the bitmap. All you have to remember is that the address is used to determine the bit to check.<\/p>\n<p>\u00b3 I sure don&#8217;t have them memorized. Each time it happens, I just re-derive it from the instructions around the crash.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>You don&#8217;t have to understand it, but you should be able to extract data from it.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-111738","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>You don&#8217;t have to understand it, but you should be able to extract data from it.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111738","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=111738"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111738\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=111738"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=111738"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=111738"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}