microsoft / microsoft/CLRInstrumentationEngine

Using IInstructionGraph::InsertBefore after applying SingleRetDefaultInstrumentation can fail to update target instructions

Open
#298 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C++
Stars
102
Forks
52
Avg merge
13h 11m
Merged PRs (30d)
6

Description

Consider an IInstrumentationMethod implementation where ShouldInstrumentMethod indicates that only System::String System::String::Concat(System::String[]) should be instrumented, and where InstrumentMethod includes the following:

    CComPtr<IInstructionFactory> pInstructionFactory;
    pMethodInfo->GetInstructionFactory(&pInstructionFactory);

    CComPtr<IInstructionGraph> pInstructionGraph;
    pMethodInfo->GetInstructions(&pInstructionGraph);

    CComPtr<ISingleRetDefaultInstrumentation> pSingleRet;
    pMethodInfo->GetSingleRetDefaultInstrumentation(&pSingleRet);
    pSingleRet->Initialize(pInstructionGraph);
    pSingleRet->ApplySingleRetDefaultInstrumentation();

    CComPtr<IInstruction> pFirstInstruction;
    pInstructionGraph->GetFirstInstruction(&pFirstInstruction);

    CComPtr<IInstruction> pNopInstr;
    pInstructionFactory->CreateInstruction(Cee_Nop, &pNopInstr);
    pInstructionGraph->InsertBefore(pFirstInstruction, pNopInstr);

In such a case, we are seeing that one of the brtrue instructions has an invalid target instruction. If pNopInstr is not added at the beginning of the method, then there are no invalid instructions.

Without inserting a nop at the beginning of the method:

    /* 0x002             */    IL_0000: ldarg.0 
    /* 0x03A      0x0011 */    IL_0001: brtrue IL_0011
    /* 0x072  0x70000662 */    IL_0006: ldstr "values"
    /* 0x073  0x06001037 */    IL_000B: newobj System.ArgumentNullException::.ctor
    /* 0x07A             */    IL_0010: throw 
    /* 0x002             */    IL_0011: ldarg.0 
    /* 0x08E             */    IL_0012: ldlen 
    /* 0x069             */    IL_0013: conv.i4 
    /* 0x017             */    IL_0014: ldc.i4.1 
    /* 0x03D      0x0047 */    IL_0015: bgt IL_0047
    /* 0x002             */    IL_001A: ldarg.0 
    /* 0x08E             */    IL_001B: ldlen 
    /* 0x039      0x0039 */    IL_001C: brfalse IL_0039
    /* 0x002             */    IL_0021: ldarg.0 
    /* 0x016             */    IL_0022: ldc.i4.0 
    /* 0x09A             */    IL_0023: ldelem.ref 
    /* 0x025             */    IL_0024: dup 
    /* 0x03A      0x003E */    IL_0025: brtrue IL_003E
    /* 0x026             */    IL_002A: pop 
    /* 0x07E  0x04000274 */    IL_002B: ldsfld System.String::Empty
    /* 0x10E       0x009 */    IL_0030: stloc 0x009
    /* 0x038      0x0128 */    IL_0034: br IL_0128
    /* 0x07E  0x04000274 */    IL_0039: ldsfld System.String::Empty
    /* 0x10E       0x009 */    IL_003E: stloc 0x009
    ...

With inserting a nop at the beginning of the method:

    /* 0x000             */    IL_0000: nop 
    /* 0x002             */    IL_0001: ldarg.0 
    /* 0x03A      0x0012 */    IL_0002: brtrue IL_0012
    /* 0x072  0x70000662 */    IL_0007: ldstr "values"
    /* 0x073  0x06001037 */    IL_000C: newobj System.ArgumentNullException::.ctor
    /* 0x07A             */    IL_0011: throw 
    /* 0x002             */    IL_0012: ldarg.0 
    /* 0x08E             */    IL_0013: ldlen 
    /* 0x069             */    IL_0014: conv.i4 
    /* 0x017             */    IL_0015: ldc.i4.1 
    /* 0x03D      0x0048 */    IL_0016: bgt IL_0048
    /* 0x002             */    IL_001B: ldarg.0 
    /* 0x08E             */    IL_001C: ldlen 
    /* 0x039      0x003A */    IL_001D: brfalse IL_003A
    /* 0x002             */    IL_0022: ldarg.0 
    /* 0x016             */    IL_0023: ldc.i4.0 
    /* 0x09A             */    IL_0024: ldelem.ref 
    /* 0x025             */    IL_0025: dup 
    /* 0x03A      0x003E */    IL_0026: brtrue IL_003E
    /* 0x026             */    IL_002B: pop 
    /* 0x07E  0x04000274 */    IL_002C: ldsfld System.String::Empty
    /* 0x10E       0x009 */    IL_0031: stloc 0x009
    /* 0x038      0x0129 */    IL_0035: br IL_0129
    /* 0x07E  0x04000274 */    IL_003A: ldsfld System.String::Empty
    /* 0x10E       0x009 */    IL_003F: stloc 0x009
   ...

Note the IL_0025: brtrue IL_003E instruction that exists without the nop. After inserting the nop, it is changed to IL_0026: brtrue IL_003E, which still targets the now-nonexistent IL_003E instruction (it should have been changed to IL_003F).

string_concat_1_without_nop.log

string_concat_2_with_nop.log

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the IInstructionGraph::InsertBefore path and the SingleRetDefaultInstrumentation sequence shown in the issue. Reproduce the String.Concat instrumentation case with and without the leading nop, then compare the generated instruction targets in the attached logs. Done means inserting before the first instruction leaves every branch target pointing to the correct instruction.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.