Skip to content

AV in GetITypeInfoForMT #75035

Closed
Closed

Description

@jkotas

See #74977 (reply in thread) for context

[Inline Frame] coreclr.dll!InterlockedCompareExchangeT(ITypeInfo * volatile *) Line 4341
[Inline Frame] coreclr.dll!InterlockedCompareExchangeT(ITypeInfo * volatile *) Line 4375
[Inline Frame] coreclr.dll!InterlockedCompareExchangeT(ITypeInfo * volatile *) Line 4406
[Inline Frame] coreclr.dll!ComMethodTable::SetITypeInfo(ITypeInfo * pNew=0x000001efb0d30fe8) Line 3853
coreclr.dll!GetITypeInfoForMT(ComMethodTable * pMT=0x00007ffd575cf440, ITypeInfo * * ppTI=0x0000001d1d17c308) Line 805
coreclr.dll!DispatchEx_GetTypeInfo(IDispatch * pDisp=0x0000001d1d17c258, unsigned int pptinfo=0x0000001d1d17c308, unsigned long) Line 1547
coreclr.dll!DispatchEx_GetTypeInfo_CallBack(void * ptr=0x0000001d1d17c258) Line 1281
coreclr.dll!DispatchEx_GetTypeInfo_Wrapper(IDispatchEx * pDisp=0x00000230477a6740, unsigned int itinfo=0x00000000, unsigned long lcid=0x00000000, ITypeInfo * * pptinfo=0x0000001d1d17c308) Line 1306
jscript.dll!JAmsi::JAmsiProcessor()
jscript.dll!VAR::InvokeByDispID()
jscript.dll!NameTbl::InvokeInternal()
jscript.dll!VAR::InvokeByDispID()
jscript.dll!CScriptRuntime::Run(class VAR *)
jscript.dll!ScrFncObj::PerformCall(class CScriptRuntime *,class VAR *,int,class VAR *,class VAR *,unsigned long)
jscript.dll!ScrFncObj::CallWithFrameOnStack(class VAR *,int,class VAR *,class VAR *,unsigned long)
jscript.dll!ScrFncObj::Call()
jscript.dll!JsEval()
jscript.dll!NatFncObj::Call()
jscript.dll!NameTbl::InvokeInternal()
jscript.dll!VAR::InvokeByDispID()
jscript.dll!CScriptRuntime::Run(class VAR *)
jscript.dll!ScrFncObj::PerformCall(class CScriptRuntime *,class VAR *,int,class VAR *,class VAR *,unsigned long)
jscript.dll!ScrFncObj::CallWithFrameOnStack(class VAR *,int,class VAR *,class VAR *,unsigned long)
jscript.dll!ScrFncObj::Call()
jscript.dll!CSession::Execute(class NameTbl *,struct tagVARIANT *,int,struct tagVARIANT *,struct tagVARIANT *,unsigned long)
jscript.dll!COleScript::ExecutePendingScripts()
jscript.dll!COleScript::ParseScriptTextCore()
jscript.dll!COleScript::ParseScriptText(unsigned short const *,unsigned short const *,struct IUnknown *,unsigned short const *,unsigned __int64,unsigned long,unsigned long,struct tagVARIANT *,struct tagEXCEPINFO *)

The debugger is pointing at the following source line in ...\src\coreclr\inc\utilcode.h

    template <typename T>
    inline T* InterlockedCompareExchangeT(
        T* volatile * destination,
        T*            exchange,
        T*            comparand)
    {
        //STATIC_ASSERT(exchange == 0);
        typedef typename std::remove_const<T>::type * non_const_ptr_t;
-->     return reinterpret_cast<T*>(InterlockedCompareExchangePointer(
            reinterpret_cast<PVOID volatile *>(const_cast<non_const_ptr_t volatile *>(destination)),
            reinterpret_cast<PVOID>(const_cast<non_const_ptr_t>(exchange)),
            reinterpret_cast<PVOID>(const_cast<non_const_ptr_t>(comparand))));
    }

Here's what the disassembler has to say:

    // Returns a NON-ADDREF'd ITypeInfo.
    HRESULT GetITypeInfoForMT(ComMethodTable *pMT, ITypeInfo **ppTI)
    {
     mov         r11,rsp  
     mov         qword ptr [r11+10h],rbx  
     mov         qword ptr [r11+18h],rsi  
     push        rdi  
     sub         rsp,20h  
        CONTRACTL
        {
            NOTHROW;
            GC_TRIGGERS;
            MODE_PREEMPTIVE;
            PRECONDITION(CheckPointer(pMT));
            PRECONDITION(CheckPointer(ppTI));
        }
        CONTRACTL_END;

        HRESULT     hr = S_OK;              // A result.
        ITypeInfo   *pTI;                   // The ITypeInfo.

        pTI = pMT->GetITypeInfo();
     mov         rax,qword ptr [rcx+28h]  
     xor         edi,edi  
     mov         qword ptr [r11+8],rax  
     mov         rsi,rdx  
     mov         rbx,rcx  
     test        rax,rax  

        if (pTI == NULL)
     jne         GetITypeInfoForMT+64h (07FFDB740A954h)  
        {
            MethodTable *pClass = pMT->GetMethodTable();

            hr = GetITypeInfoForEEClass(pClass, &pTI);
     mov         rcx,qword ptr [rcx+8]  
     lea         rdx,[r11+8]  
     xor         r8d,r8d  
     call        GetITypeInfoForEEClass (07FFDB7409A70h)  
     mov         edi,eax  

            if (SUCCEEDED(hr))
     test        eax,eax  
     js          GetITypeInfoForMT+5Fh (07FFDB740A94Fh)  
            {
                pMT->SetITypeInfo(pTI);
     mov         rcx,qword ptr [pTI]  
     xor         eax,eax  
-->  lock cmpxchg qword ptr [rbx+28h],rcx  
     jne         GetITypeInfoForMT+4Fh (07FFDB740A93Fh)  
     call        SafeAddRef (07FFDB711731Ch)  
                SafeReleasePreemp(pTI);
     mov         rcx,qword ptr [pTI]  
     call        SafeReleasePreemp (07FFDB7119290h)  
                pTI = pMT->GetITypeInfo();
     mov         rax,qword ptr [rbx+28h]  
     jmp         GetITypeInfoForMT+64h (07FFDB740A954h)  

            if (SUCCEEDED(hr))
     mov         rax,qword ptr [pTI]  
            }
        }

        *ppTI = pTI;
        return hr;
    }
     mov         rbx,qword ptr [rsp+38h]  
     mov         qword ptr [rsi],rax  
     mov         eax,edi  
     mov         rsi,qword ptr [rsp+40h]  
     add         rsp,20h  
     pop         rdi  
     ret  

Just for completeness, [rbx+28h] is pointing at two consecutive 64-bit nulls followed by other data.

One other bit: The debugger indicates that the access violation is due to an invalid write.

Activity

  1. ghost added
    untriagedNew issue has not been triaged by the area owner
    on Sep 2, 2022
  2. added and removed
    untriagedNew issue has not been triaged by the area owner
    on Sep 2, 2022
  3. added this to the 7.0.0 milestone on Sep 2, 2022
  4. AaronRobinsonMSFT commented on Sep 3, 2022

    @AaronRobinsonMSFT
    Member

    @jkotas Is there any prior art to this? I don't have a full repro but do see the issue in the x86 DMP. Do you know how to check the write status of the page in WinDBG? I'm going to try to figure it out, but any pointers are helpful.

  5. AaronRobinsonMSFT commented on Sep 3, 2022

    @AaronRobinsonMSFT
    Member

    @jkotas Seems your intuition is correct.

    0:000> !vprot 048fe794-18h
    BaseAddress:       048fd000
    AllocationBase:    048f0000
    AllocationProtect: 00000040  PAGE_EXECUTE_READWRITE
    RegionSize:        00003000
    State:             00001000  MEM_COMMIT
    Protect:           00000020  PAGE_EXECUTE_READ <--- Not writable
    Type:              00040000  MEM_MAPPED
    
  6. AaronRobinsonMSFT commented on Sep 3, 2022

    @AaronRobinsonMSFT
    Member

    Assuming I should use ClrVirtualProtect. However, I am not sure the best way to address this in a holistic way as I am dubious this is the only place where we are missing this check. Should I perhaps create a general "get member memory for set" on the ComMethodTable or just handle this in a one off for the specific ITypeInfo update path?

  7. jkotas commented on Sep 3, 2022

    @jkotas
    MemberAuthor

    ExecutableWriterHolder is the helper you are looking for.

  8. ghost added
    in-prThere is an active PR which will close this issue when it is merged
    on Sep 3, 2022
  9. ghost removed
    in-prThere is an active PR which will close this issue when it is merged
    on Sep 5, 2022
  10. ghost locked as resolved and limited conversation to collaborators on Oct 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions