Skip to content

JIT: potential GC hole with collectible code in delegate #105082

@huoyaoyuan

Description

@huoyaoyuan

Originally discovered in #104731 (comment)

When a delegate references collectible code (dynamic method or collectible assembly), and being the only thing keeping the code alive, the code may be collected before invoked.

Consider the following method:

        [MethodImpl(MethodImplOptions.NoInlining)]
        static void Test(Action<int> action, int counts)
        {
            int sum = 0;
            for (int i = 0; i < counts; i++)
            {
                sum += i;
            }
            action(sum);
        }

Current codegen on arm64:

; Assembly listing for method CSPlayground.Program:Test(System.Action`1[int],int) (FullOpts)
; Emitting BLENDED_CODE for generic ARM64 - Windows
; FullOpts code
; optimized code
; fp based frame
; fully interruptible
; No PGO data
; invoked as altjit
; Final local variable assignments
;
;  V00 arg0         [V00,T03] (  4,  4   )     ref  ->   x2         class-hnd single-def <System.Action`1[int]>
;  V01 arg1         [V01,T02] (  4,  7   )     int  ->   x1         single-def
;  V02 loc0         [V02,T01] (  4, 10   )     int  ->   x0        
;  V03 loc1         [V03,T00] (  5, 17   )     int  ->   x3        
;# V04 OutArgs      [V04    ] (  1,  1   )  struct ( 0) [sp+0x00]  do-not-enreg[XS] addr-exposed "OutgoingArgSpace"
;
; Lcl frame size = 0

G_M16562_IG01:        ; bbWeight=1, gcrefRegs=0000 {}, byrefRegs=0000 {}, byref, nogc <-- Prolog IG
            stp     fp, lr, [sp, #-0x10]!
            mov     fp, sp
            mov     x2, x0
                             ; gcrRegs +[x2]
						;; size=12 bbWeight=1 PerfScore 2.00
G_M16562_IG02:        ; bbWeight=1, gcrefRegs=0004 {x2}, byrefRegs=0000 {}, byref, isz, align
            mov     w0, wzr
            mov     w3, wzr
            cmp     w1, #0
            ble     G_M16562_IG04
            align   [4 bytes for IG03]
            align   [0 bytes]
            align   [0 bytes]
            align   [0 bytes]
						;; size=20 bbWeight=1 PerfScore 3.00
G_M16562_IG03:        ; bbWeight=4, gcrefRegs=0004 {x2}, byrefRegs=0000 {}, loop=IG03, byref, isz
            add     w0, w0, w3
            add     w3, w3, #1
            cmp     w3, w1
            blt     G_M16562_IG03
						;; size=16 bbWeight=4 PerfScore 10.00
G_M16562_IG04:        ; bbWeight=1, gcrefRegs=0004 {x2}, byrefRegs=0000 {}, byref
            mov     w1, w0
            ldr     x0, [x2, #0x08]
                             ; gcrRegs +[x0]
            ldr     x2, [x2, #0x18]
                             ; gcrRegs -[x2]
            blr     x2
                             ; gcrRegs -[x0]
                             ; gcr arg pop 0
						;; size=16 bbWeight=1 PerfScore 7.50
G_M16562_IG05:        ; bbWeight=1, epilog, nogc, extend
            ldp     fp, lr, [sp], #0x10
            ret     lr
						;; size=8 bbWeight=1 PerfScore 2.00

; Total bytes of code 72, prolog size 12, PerfScore 24.50, instruction count 21, allocated bytes for code 72 (MethodHash=da6bbf4d) for method CSPlayground.Program:Test(System.Action`1[int],int) (FullOpts)

The ldr x2, [x2, #0x18] instruction loads the function pointer from the delegate object. Now no register holds the delegate object, and GC is not aware of the function pointer in x2. If GC happens before the next blr x2 instruction, it may collect the delegate object and associated code.

The loop in front of the invocation makes the method fully interruptible, and thus GC can happen at any instruction except prolog/epilog.

All RISC architectures should be suffered from this issue. In xarch, loading the function pointer can be contained in the call instruction like call [rax+0x18], so GC won't lose the track.

Metadata

Metadata

Assignees

Labels

Priority:2Work that is important, but not critical for the releasearea-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIin-prThere is an active PR which will close this issue when it is merged

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions