File History: access violation exception (0xC0000005) in fhcfg.dll


This exception occurs when you try to save changes in the Control Panel\System and Security\File History\Exclude Folders.

Let’s display information about the exception, the version of the faulting module and then change the context to the context of the exception.

0:033> !analyze -v
...
FAULTING_IP: 
fhcfg!CDpScopeIterator::GetItem+78
00007ff9`05c18148 4883791808      cmp     qword ptr [rcx+18h],8
EXCEPTION_RECORD:  ffffffffffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 00007ff905c18148 (fhcfg!CDpScopeIterator::GetItem+0x0000000000000078)
   ExceptionCode: c0000005 (Access violation)
...
MODULE_NAME: fhcfg
IMAGE_NAME:  fhcfg.dll
...
0:033> lmvm fhcfg
    Image path: C:\Windows\System32\fhcfg.dll
    Image name: fhcfg.dll
    Timestamp:        Sat Feb 22 12:17:32 2014 (5308793C)
    CheckSum:         000ADCA8
    ImageSize:        000AE000
    File version:     6.3.9600.17031
    Product version:  6.3.9600.17031
    File flags:       0 (Mask 3F)
    File OS:          40004 NT Win32
    File type:        2.0 Dll
    File date:        00000000.00000000
    Translations:     0409.04b0
    CompanyName:      Microsoft Corporation
    ProductName:      Microsoft® Windows® Operating System
    InternalName:     fhcfg.dll
    OriginalFilename: fhcfg.dll
    ProductVersion:   6.3.9600.17031
    FileVersion:      6.3.9600.17031 (winblue_gdr.140221-1952)
    FileDescription:  File History Configuration Manager
    LegalCopyright:   © Microsoft Corporation. All rights reserved.
0:033> .ecxr
rax=0055005c003a0043 rbx=0000000000000000 rcx=0055005c003a0053
rdx=0000000004ece070 rsi=0000000004ece0a8 rdi=0000000004ece070
rip=00007ff905c18148 rsp=0000000004ecdff0 rbp=0000000000000000
 r8=0000000000000000  r9=0000000000000006 r10=0000000000000080
r11=0000000004ecdf30 r12=0000000000000000 r13=0000000000040001
r14=0000000000040000 r15=0000000000000007
iopl=0         nv up ei pl nz na pe nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
fhcfg!CDpScopeIterator::GetItem+0x78:
00007ff9`05c18148 4883791808      cmp     qword ptr [rcx+18h],8 ds:0055005c`003a006b=????????????????

Now we can walk back to the beginning of the method to find out where rcx came from.

0:033> ? @rcx-10
Evaluate expression: 23925768161198147 = 0055005c`003a0043
0:033> u fhcfg!CDpScopeIterator::GetItem fhcfg!CDpScopeIterator::GetItem+0x78
fhcfg!CDpScopeIterator::GetItem:
00007ff9`05c180d0 48895c2408      mov     qword ptr [rsp+8],rbx
00007ff9`05c180d5 57              push    rdi
00007ff9`05c180d6 4883ec20        sub     rsp,20h
00007ff9`05c180da 33db            xor     ebx,ebx
00007ff9`05c180dc 488bfa          mov     rdi,rdx
00007ff9`05c180df 4885d2          test    rdx,rdx
00007ff9`05c180e2 753a            jne     fhcfg!CDpScopeIterator::GetItem+0x4e (00007ff9`05c1811e)
00007ff9`05c180e4 488b0d5d9f0400  mov     rcx,qword ptr [fhcfg!WPP_GLOBAL_Control (00007ff9`05c62048)]
00007ff9`05c180eb 488d05569f0400  lea     rax,[fhcfg!WPP_GLOBAL_Control (00007ff9`05c62048)]
00007ff9`05c180f2 483bc8          cmp     rcx,rax
00007ff9`05c180f5 7420            je      fhcfg!CDpScopeIterator::GetItem+0x47 (00007ff9`05c18117)
00007ff9`05c180f7 f6411c01        test    byte ptr [rcx+1Ch],1
00007ff9`05c180fb 741a            je      fhcfg!CDpScopeIterator::GetItem+0x47 (00007ff9`05c18117)
00007ff9`05c180fd 488b4910        mov     rcx,qword ptr [rcx+10h]
00007ff9`05c18101 8d5742          lea     edx,[rdi+42h]
00007ff9`05c18104 4c8d0dd5f0faff  lea     r9,[fhcfg!`string' (00007ff9`05bc71e0)]
00007ff9`05c1810b 4c8d054ef6faff  lea     r8,[fhcfg!WPP_60d49f496dd11a03f0fd8e5dc827deca_Traceguids (00007ff9`05bc7760)]
00007ff9`05c18112 e871deffff      call    fhcfg!WPP_SF_s (00007ff9`05c15f88)
00007ff9`05c18117 b857000780      mov     eax,80070057h
00007ff9`05c1811c eb4a            jmp     fhcfg!CDpScopeIterator::GetItem+0x98 (00007ff9`05c18168)
00007ff9`05c1811e 395960          cmp     dword ptr [rcx+60h],ebx
00007ff9`05c18121 7410            je      fhcfg!CDpScopeIterator::GetItem+0x63 (00007ff9`05c18133)
00007ff9`05c18123 488b4140        mov     rax,qword ptr [rcx+40h]
00007ff9`05c18127 483b4148        cmp     rax,qword ptr [rcx+48h]
00007ff9`05c1812b 7410            je      fhcfg!CDpScopeIterator::GetItem+0x6d (00007ff9`05c1813d)
00007ff9`05c1812d 488d4838        lea     rcx,[rax+38h]
00007ff9`05c18131 eb15            jmp     fhcfg!CDpScopeIterator::GetItem+0x78 (00007ff9`05c18148)
00007ff9`05c18133 488b4150        mov     rax,qword ptr [rcx+50h]
00007ff9`05c18137 483b4158        cmp     rax,qword ptr [rcx+58h]
00007ff9`05c1813b 7507            jne     fhcfg!CDpScopeIterator::GetItem+0x74 (00007ff9`05c18144)
00007ff9`05c1813d bb90040780      mov     ebx,80070490h
00007ff9`05c18142 eb22            jmp     fhcfg!CDpScopeIterator::GetItem+0x96 (00007ff9`05c18166)
00007ff9`05c18144 488d4810        lea     rcx,[rax+10h]    <<<< rcx-10 == rax
00007ff9`05c18148 4883791808      cmp     qword ptr [rcx+18h],8

Now we know that rcx was changed just once at the 00007ff9`05c18144 address. If we’ll examine the caller of this method we’ll see that rcx was populated from the stack with the pointer to an object of the IFhScopeIterator interface.

0:033> ub fhcpl!AddListItemsToExcludes+0x15a
fhcpl!AddListItemsToExcludes+0x142:
00007ff9`196cb812 8bd8            mov     ebx,eax
00007ff9`196cb814 85c0            test    eax,eax
00007ff9`196cb816 7858            js      fhcpl!AddListItemsToExcludes+0x1a0 (00007ff9`196cb870)
00007ff9`196cb818 754b            jne     fhcpl!AddListItemsToExcludes+0x195 (00007ff9`196cb865)
00007ff9`196cb81a 488b4c2440      mov     rcx,qword ptr [rsp+40h]
00007ff9`196cb81f 488d542450      lea     rdx,[rsp+50h]
00007ff9`196cb824 488b01          mov     rax,qword ptr [rcx]
00007ff9`196cb827 ff5020          call    qword ptr [rax+20h]
0:033> k L4
 # Child-SP          RetAddr           Call Site
00 00000000`04ecdff0 00007ff9`196cb82a fhcfg!CDpScopeIterator::GetItem+0x78
01 00000000`04ece020 00007ff9`196cc30e fhcpl!AddListItemsToExcludes+0x15a
02 00000000`04ece060 00007ff9`196cd00a fhcpl!CExcludeFoldersPage::OnEvent+0x146
03 00000000`04ece0a0 00007ff9`1d1ec3c6 fhcpl!CHistoryVaultPage::OnEvent+0x26
...
0:033> dps poi(00000000`04ece020+40)
00000000`106fc790  00007ff9`05bc2c88 fhcfg!ATL::CComObject<CDpScopeIterator>::`vftable'
00000000`106fc798  00000000`00000001
00000000`106fc7a0  ffffffff`ffffffff
00000000`106fc7a8  00000000`ffffffff
00000000`106fc7b0  00000000`00000000
00000000`106fc7b8  00000000`00000000
00000000`106fc7c0  00000000`020007d0
00000000`106fc7c8  00000000`10790001
00000000`106fc7d0  00000000`00000000
00000000`106fc7d8  00000000`00000000
00000000`106fc7e0  0055005c`003a0043    <<<< rcx+50
00000000`106fc7e8  00000000`106dc1c0
00000000`106fc7f0  00000000`00000000
00000000`106fc7f8  80002200`4cb849aa
00000000`106fc800  00000000`106fcaa0
00000000`106fc808  00000000`106fcb80

To see what data should be in [rcx+50] location, let’s launch debugger and set a breakpoint on fhcpl!AddListItemsToExcludes+0x11c function.

0:011> bp fhcpl!AddListItemsToExcludes+0x11c
fhcpl!AddListItemsToExcludes+0x11c:
00007ffe`045cb7ec 48895c2410      mov     qword ptr [rsp+10h],rbx ss:00000000`02cae258=0000000005421c60
00007ffe`045cb7f1 55              push    rbp
00007ffe`045cb7f2 56              push    rsi
00007ffe`045cb7f3 57              push    rdi
00007ffe`045cb7f4 4883ec20        sub     rsp,20h
00007ffe`045cb7f8 488bf1          mov     rsi,rcx
00007ffe`045cb7fb 488b09          mov     rcx,qword ptr [rcx] ds:00000000`02cae298=0000000000aba960
00007ffe`045cb7fe 8bfa            mov     edi,edx
00007ffe`045cb800 488b01          mov     rax,qword ptr [rcx] ds:00000000`00aba960={fhcfg!ATL::CComObject<CDpConfigMgr>::`vftable' (00007ffd`ef502a78)}
00007ffe`045cb803 448bc2          mov     r8d,edx
00007ffe`045cb806 4c8d4c2440      lea     r9,[rsp+40h] <<<< Will hold the pointer to an object of the IFhScopeIterator interface
00007ffe`045cb80b 33d2            xor     edx,edx
00007ffe`045cb80d ff5038          call    qword ptr [rax+38h] ds:00007ffd`ef502ab0={fhcfg!CDpConfigMgr::GetIncludeExcludeRules (00007ffd`ef558cfc)}
00007ffe`045cb810 33ed            xor     ebp,ebp
0:004> dps poi(@rsp+40)
00000000`04c64ba0  00007ffd`ef502c88 fhcfg!ATL::CComObject<CDpScopeIterator>::`vftable'
00000000`04c64ba8  00000000`00000001
00000000`04c64bb0  ffffffff`ffffffff
00000000`04c64bb8  00000000`ffffffff
00000000`04c64bc0  00000000`00000000
00000000`04c64bc8  00000000`00000000
00000000`04c64bd0  00000000`020007d0
00000000`04c64bd8  00000000`04c60001
00000000`04c64be0  00000000`00000000
00000000`04c64be8  00000000`00000000
00000000`04c64bf0  00000000`04c61700 <<<< This is the head of the doubly linked list of the excluded libraries (LIST_ENTRY structure).
00000000`04c64bf8  00000000`04c616c0
00000000`04c64c00  00000000`00000000
00000000`04c64c08  80002100`354bc912
00000000`04c64c10  00000000`04c649e0
00000000`04c64c18  00000000`04c64740
0:004> dl poi(poi(@rsp+40)+50) 10 2  <<<< The linked list.
00000000`04c61700  00000000`04c61b00 00000000`04c616c0
00000000`04c61b00  00000000`04c61400 00000000`04c61700
00000000`04c61400  00000000`04c61680 00000000`04c61b00
00000000`04c61680  00000000`04c616c0 00000000`04c61400
00000000`04c616c0  00000000`04c61700 00000000`04c61680
0:004> !list -x "du poi(@$extret+10)" poi(poi(@rsp+40)+50) <<<< GUIDs of the libraries in the list.
00000000`04c63940  "*7b0db17d-9cd2-4a93-9733-46cc890"
00000000`04c63980  "22e7c"
00000000`04c639a0  "*2112ab0a-c86a-4ffe-a368-0de96e4"
00000000`04c639e0  "7012e"
00000000`04c63a00  "*a990ae9f-a03b-4e80-94bc-9912d75"
00000000`04c63a40  "04104"
00000000`04c63a60  "*491e922f-5643-4af4-a7eb-4e7a138"
00000000`04c63aa0  "d8174"
0068006f`004a005c  "????????????????????????????????"

Let's set a breakpoint for write access on the head of the list. The breakpoint hit in the fhcfg!CDpScopeIterator::MoveToNextItem method but we can see that this method is not a culprit because it takes the wrong value from the first entry of the linked list and saves it into the head of the list.

0:004> ba w8 poi(@rsp+40)+50
0:004> ub .
fhcfg!CDpScopeIterator::MoveToNextItem+0x50:
00007ffd`ef558098 7508            jne     fhcfg!CDpScopeIterator::MoveToNextItem+0x5a (00007ffd`ef5580a2)
00007ffd`ef55809a 41b890040780    mov     r8d,80070490h
00007ffd`ef5580a0 eb23            jmp     fhcfg!CDpScopeIterator::MoveToNextItem+0x7d (00007ffd`ef5580c5)
00007ffd`ef5580a2 488b4150        mov     rax,qword ptr [rcx+50h]
00007ffd`ef5580a6 418bd0          mov     edx,r8d
00007ffd`ef5580a9 41b890040780    mov     r8d,80070490h
00007ffd`ef5580af 488b08          mov     rcx,qword ptr [rax]
00007ffd`ef5580b2 49894950        mov     qword ptr [r9+50h],rcx
0:004> r
rax=0000000004c61700 rbx=0000000000000000 rcx=0055005c003a0043
rdx=0000000000000000 rsi=0000000002cae298 rdi=0000000000000001
rip=00007ffdef5580b6 rsp=0000000002cae208 rbp=0000000000000000
 r8=0000000080070490  r9=0000000004c64ba0 r10=00007ffe1e8538d0
r11=0000000002cae120 r12=0000000000000000 r13=0000000000040001
r14=0000000000040000 r15=0000000000000007
iopl=0         nv up ei pl nz na pe nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
fhcfg!CDpScopeIterator::MoveToNextItem+0x6e:
00007ffd`ef5580b6 498b4958        mov     rcx,qword ptr [r9+58h] ds:00000000`04c64bf8=0000000004c616c0
0:004> dps 0000000004c64ba0
00000000`04c64ba0  00007ffd`ef502c88 fhcfg!ATL::CComObject<CDpScopeIterator>::`vftable'
00000000`04c64ba8  00000000`00000001
00000000`04c64bb0  ffffffff`ffffffff
00000000`04c64bb8  00000000`ffffffff
00000000`04c64bc0  00000000`00000000
00000000`04c64bc8  00000000`00000000
00000000`04c64bd0  00000000`020007d0
00000000`04c64bd8  00000000`04c60001
00000000`04c64be0  00000000`00000000
00000000`04c64be8  00000000`00000000
00000000`04c64bf0  0055005c`003a0043    <<<< r9+50h
00000000`04c64bf8  00000000`04c616c0
00000000`04c64c00  00000000`00000000
00000000`04c64c08  88002100`354bc912
00000000`04c64c10  00000000`04c62550
00000000`04c64c18  00000000`04c64200
0:004> k L4
Child-SP          RetAddr           Call Site
00000000`02cae208 00007ffe`045cb854 fhcfg!CDpScopeIterator::MoveToNextItem+0x6e
00000000`02cae210 00007ffe`045cc30e fhcpl!AddListItemsToExcludes+0x184
00000000`02cae250 00007ffe`045cd00a fhcpl!CExcludeFoldersPage::OnEvent+0x146
00000000`02cae290 00007ffe`1a14c3c6 fhcpl!CHistoryVaultPage::OnEvent+0x26

Now let’s set a breakpoint for write access on the first entry in the list.

0:004> ba w8 poi(poi(@rsp+40)+50)
0:004> dps poi(@rsp+40)
00000000`05523ef0  00007ffd`ef502c88 fhcfg!ATL::CComObject<CDpScopeIterator>::`vftable'
00000000`05523ef8  00000000`00000001
00000000`05523f00  ffffffff`ffffffff
00000000`05523f08  00000000`ffffffff
00000000`05523f10  00000000`00000000
00000000`05523f18  00000000`00000000
00000000`05523f20  00000000`020007d0
00000000`05523f28  00000000`05520001
00000000`05523f30  00000000`00000000
00000000`05523f38  00000000`00000000
00000000`05523f40  00000000`05521600
00000000`05523f48  00000000`05521b00
00000000`05523f50  00000001`00000000
00000000`05523f58  80000400`4ddf0f31
00000000`05523f60  00000000`05523f60
00000000`05523f68  00000000`05523f60
0:004> g
Breakpoint 1 hit
fhcfg!std::basic_string<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >::_Copy+0x146:
00007ffd`ef560a42 4883c460        add     rsp,60h
0:004> ub .
fhcfg!std::basic_string<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >::_Copy+0x125:
00007ffd`ef560a21 488b0b          mov     rcx,qword ptr [rbx]
00007ffd`ef560a24 ff15467e0400    call    qword ptr [fhcfg!_imp_??3YAXPEAXZ (00007ffd`ef5a8870)]
00007ffd`ef560a2a 4c8933          mov     qword ptr [rbx],r14
00007ffd`ef560a2d 48897b18        mov     qword ptr [rbx+18h],rdi
00007ffd`ef560a31 4c897b10        mov     qword ptr [rbx+10h],r15
00007ffd`ef560a35 4883ff08        cmp     rdi,8
00007ffd`ef560a39 490f43de        cmovae  rbx,r14
00007ffd`ef560a3d 664289347b      mov     word ptr [rbx+r15*2],si
0:004> r
rax=0000000005521600 rbx=0000000005521600 rcx=0000000000000200
rdx=0000000000060011 rsi=0000000000000000 rdi=0000000000000017
rip=00007ffdef560a42 rsp=000000000310db00 rbp=0000000000000000
 r8=0000000000000005  r9=0000000000000007 r10=00000000055213b0
r11=0000000000000000 r12=00000000055222e8 r13=000000000310dcf0
r14=0000000005521600 r15=0000000000000000
iopl=0         nv up ei pl nz ac po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000216
fhcfg!std::basic_string<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >::_Copy+0x146:
00007ffd`ef560a42 4883c460        add     rsp,60h
0:004> k
Child-SP          RetAddr           Call Site
00000000`0310db00 00007ffd`ef5607d8 fhcfg!std::basic_string<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >::_Copy+0x146
00000000`0310db90 00007ffd`ef576846 fhcfg!std::basic_string<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >::assign+0x84
00000000`0310dbd0 00007ffd`ef572526 fhcfg!DpConfigHelper::AddFolder+0x12e
00000000`0310dcb0 00007ffd`ef5729fb fhcfg!DpConfigHelper::AddExcludeFolder+0x106
00000000`0310dd50 00007ffd`ef55c933 fhcfg!DpConfigHelper::RefreshCurrentExcludes+0xef
00000000`0310de40 00007ffd`ef558c8a fhcfg!CDpConfigMgr::AddRemoveLibraryExclude+0x97
00000000`0310de70 00007ffe`045cb7ac fhcfg!CDpConfigMgr::AddRemoveExcludeRule+0x1e6
00000000`0310df00 00007ffe`045cc32d fhcpl!AddListItemsToExcludes+0xdc
00000000`0310e1d0 00007ffe`045cd00a fhcpl!CExcludeFoldersPage::OnEvent+0x165
00000000`0310e210 00007ffe`1a14c3c6 fhcpl!CHistoryVaultPage::OnEvent+0x26

Someone freed the memory of the item because the fhcfg!std::basic_string method simply copies the string into the memory allocated at the same address. Here is the method that freed the memory.

0:004> uf /c fhcfg!CDpConfigMgr::AddRemoveLibraryExclude
fhcfg!CDpConfigMgr::AddRemoveLibraryExclude (00007ffd`ef55c89c)
  fhcfg!CDpConfigMgr::AddRemoveLibraryExclude+0x28 (00007ffd`ef55c8c4):
    call to fhcfg!DpConfigHelper::AddExcludeLibrary (00007ffd`ef57262c)
  fhcfg!CDpConfigMgr::AddRemoveLibraryExclude+0x5f (00007ffd`ef55c8fb):
    call to fhcfg!DpConfigHelper::RemoveExcludeLibrary (00007ffd`ef572794)
  fhcfg!CDpConfigMgr::AddRemoveLibraryExclude+0x92 (00007ffd`ef55c92e):
    call to fhcfg!DpConfigHelper::RefreshCurrentExcludes (00007ffd`ef57290c)
  fhcfg!CDpConfigMgr::AddRemoveLibraryExclude+0xcd (00007ffd`ef55c969):
    call to fhcfg!WPP_SF_d (00007ffd`ef555f04)

So, the File History APIs work correctly and the flaw is in the fhcpl!AddListItemsToExcludes+0x11c function which removes items from an exclusion list. That's how the function removes items:

#include <fhcfg.h>

VOID RemoveAllRules(
    IFhConfigMgr *ConfigMgr,
    FH_PROTECTED_ITEM_CATEGORY Category
    )
{
    HRESULT hr;
    IFhScopeIterator *ScopeIterator = NULL;

    hr = ConfigMgr->GetIncludeExcludeRules(FALSE, Category, &ScopeIterator);

    if (SUCCEEDED(hr)) {

        BSTR Item = NULL;

        do {

            //
            // Get the name of the item.
            // Here will be an access violation exception after one or more iterations.
            // Wrong pointer in the list head (LIST_ENTRY.Flink) in the ScopeIterator object.
            //

            hr = ScopeIterator->GetItem(&Item);

            if (SUCCEEDED(hr)) {

                //
                // 1. Removes the item from the doubly linked list
                // 2. Frees the memory of the item.
                // 3. Allocates new memory for its own use (sometimes memory is allocated at the same
                //    address where was the removed item)
                // 4. Overwrites the old data.
                //

                hr = ConfigMgr->AddRemoveExcludeRule(FALSE, Category, Item);

                if (SUCCEEDED(hr)) {

                    //
                    // Takes the wrong pointer from the LIST_ENTRY.Flink of the removed item and
                    // saves it in the list head (LIST_ENTRY.Flink) in the ScopeIterator object.
                    //

                    hr = ScopeIterator->MoveToNextItem();
                }

                SysFreeString(Item);
            }

        } while (SUCCEEDED(hr));

        ScopeIterator->Release();
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hr;
    IFhConfigMgr *ConfigMgr = NULL;

    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

    hr = CoCreateInstance(__uuidof(FhConfigMgr), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&ConfigMgr));

    if (SUCCEEDED(hr)) {

        hr = ConfigMgr->LoadConfiguration();

        if (SUCCEEDED(hr)) {

            RemoveAllRules(ConfigMgr, FH_FOLDER);
            RemoveAllRules(ConfigMgr, FH_LIBRARY);

            //AddListItemsToExcludes(...);

            ConfigMgr->SaveConfiguration();
        }

        ConfigMgr->Release();
    }

    CoUninitialize();

    return 0;
}

As a temporary solution, I wrote a tool for adding to or removing from Excluded folders and libraries. You can download it from here.