Skip to content

JIT: STRESS_OPT_REPEAT silent miscompile produces wrong-direction branch into divide-by-zero #129386

@AndyAyersMS

Description

@AndyAyersMS

Note

This issue was filed by GitHub Copilot CLI on @AndyAyersMS's machine.
The repro is verified locally on a current main (
untime-a build with both PR #129348 and PR #129136 applied).

Summary

Under DOTNET_JitEnableOptRepeat=1 (or DOTNET_JitStress=1/2/DOTNET_JitStressModeNames=STRESS_OPT_REPEAT) the JIT silently miscompiles a uint-comparison branch, taking the wrong direction so control flow lands on a % 0 that throws DivideByZeroException. Just one extra optimization pass (DOTNET_JitOptRepeatCount=2) is sufficient -- so this is an idempotency bug: the second pass over already-optimized IR produces wrong code.

Same general shape as the recently-filed #129288 (wrong-direction branch into a divide-by-zero path) but a different mechanism. Surfaced by an experimental in-development fuzzer (ReifyCs).

Minimal repro env

DOTNET_TieredCompilation=0 DOTNET_JitEnableOptRepeat=1 DOTNET_JitOptRepeat=Fn30000002142 DOTNET_JitOptRepeatCount=2

Run the program below under corerun -- it throws

Unhandled exception. System.DivideByZeroException: Attempted to divide by zero. at Program.Fn30000002142(UInt32 p0, UInt32 p1) at Program.Main()

Without JitOptRepeat, or under MinOpts, the program returns 0 with no exception.

Why it's a JIT bug

The very first statement of the function is

csharp v6 = (0u < p0) ? 1 : 0; if (v6 != 0) goto b1; goto b3;

With Input_p0 = 0u, v6 == 0 and the branch must fall through to b3. The buggy code takes goto b1 instead, and eventually reaches block b2 which contains v19 = v5 % p0; -- a divide by zero because p0 == 0. Block b2 is unreachable under the source semantics; with DOTNET_JitOptRepeat disabled, every config (tier0_min, fullopts, tier1_pgo, runtime_async, stress2, stressregs) prints 00000000 and exits 0.

Stress flag bisection (windows.x64 Checked, runtime-a)

Setting Result
default PASS
DOTNET_JitStress=1 CRASH
DOTNET_JitStress=2 CRASH
DOTNET_JitStressModeNames=STRESS_OPT_REPEAT CRASH
DOTNET_JitOptRepeatCount=2 (with JitEnableOptRepeat=1 + JitOptRepeat=Fn30000002142) CRASH
All other individual STRESS_* modes tried (REVERSE_FLAG, MERGED_RETURNS, OPT_BOOLS_GC, OPT_BOOLS_COMPARE_CHAIN_COST, BB_PROFILE, DO_WHILE_LOOPS, REGS, REMORPH_TREES, MAKE_CSE, CLONE_EXPR, FOLD, DOWNWARDS_COUNTED_LOOPS, STRENGTH_REDUCTION, SPLIT_TREES_RANDOMLY, EMITTER) PASS

So this is specifically a JitOptRepeat idempotency bug.

Environment

Repro source

Program.cs (243 lines, autogenerated)
// Generated by ReifyCs. Do not edit by hand.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;

public static class Program
{
    private static volatile uint Input_p0 = unchecked((uint)0x00000000);
    private static volatile uint Input_p1 = unchecked((uint)0xFFFFFFFF);
    private static volatile uint Sink;
    private static int[] __trace = new int[64];
    private static int __traceLen = 0;

    [MethodImpl(MethodImplOptions.NoInlining)]
    public static uint Fn30000002142(uint p0, uint p1)
    {
        unchecked
        {
            uint v1 = unchecked((uint)0x00000000), v2 = unchecked((uint)0x00000000), v3 = unchecked((uint)0x00000000), v4 = unchecked((uint)0x00000000), v5 = unchecked((uint)0x00000000), v7 = unchecked((uint)0x00000000), v9 = unchecked((uint)0x00000000), v11 = unchecked((uint)0x00000000), v13 = unchecked((uint)0x00000000), v14 = unchecked((uint)0x00000000), v16 = unchecked((uint)0x00000000), v18 = unchecked((uint)0x00000000), v19 = unchecked((uint)0x00000000), v20 = unchecked((uint)0x00000000), v22 = unchecked((uint)0x00000000), v23 = unchecked((uint)0x00000000), v24 = unchecked((uint)0x00000000), v25 = unchecked((uint)0x00000000), v27 = unchecked((uint)0x00000000), v28 = unchecked((uint)0x00000000), v29 = unchecked((uint)0x00000000), v31 = unchecked((uint)0x00000000), v32 = unchecked((uint)0x00000000), v33 = unchecked((uint)0x00000000), v35 = unchecked((uint)0x00000000), v37 = unchecked((uint)0x00000000), v39 = unchecked((uint)0x00000000), v40 = unchecked((uint)0x00000000), v41 = unchecked((uint)0x00000000), v42 = unchecked((uint)0x00000000), v43 = unchecked((uint)0x00000000), v44 = unchecked((uint)0x00000000), v47 = unchecked((uint)0x00000000), v49 = unchecked((uint)0x00000000), v50 = unchecked((uint)0x00000000), v51 = unchecked((uint)0x00000000), v52 = unchecked((uint)0x00000000), v54 = unchecked((uint)0x00000000), v55 = unchecked((uint)0x00000000), v56 = unchecked((uint)0x00000000), v57 = unchecked((uint)0x00000000), v58 = unchecked((uint)0x00000000), v61 = unchecked((uint)0x00000000), v62 = unchecked((uint)0x00000000), v63 = unchecked((uint)0x00000000), v65 = unchecked((uint)0x00000000), v66 = unchecked((uint)0x00000000), v67 = unchecked((uint)0x00000000), v68 = unchecked((uint)0x00000000), v69 = unchecked((uint)0x00000000), v71 = unchecked((uint)0x00000000), v72 = unchecked((uint)0x00000000), v73 = unchecked((uint)0x00000000), v74 = unchecked((uint)0x00000000), v75 = unchecked((uint)0x00000000), v78 = unchecked((uint)0x00000000), v80 = unchecked((uint)0x00000000), v82 = unchecked((uint)0x00000000), v83 = unchecked((uint)0x00000000), v84 = unchecked((uint)0x00000000), v85 = unchecked((uint)0x00000000), v86 = unchecked((uint)0x00000000), v87 = unchecked((uint)0x00000000), v88 = unchecked((uint)0x00000000), v89 = unchecked((uint)0x00000000), v91 = unchecked((uint)0x00000000), v93 = unchecked((uint)0x00000000), v94 = unchecked((uint)0x00000000), v96 = unchecked((uint)0x00000000), v97 = unchecked((uint)0x00000000), v99 = unchecked((uint)0x00000000), v100 = unchecked((uint)0x00000000), v101 = unchecked((uint)0x00000000), v102 = unchecked((uint)0x00000000), v103 = unchecked((uint)0x00000000), v104 = unchecked((uint)0x00000000), v105 = unchecked((uint)0x00000000), v107 = unchecked((uint)0x00000000), v108 = unchecked((uint)0x00000000), v109 = unchecked((uint)0x00000000), v111 = unchecked((uint)0x00000000), v112 = unchecked((uint)0x00000000), v113 = unchecked((uint)0x00000000), v114 = unchecked((uint)0x00000000), v115 = unchecked((uint)0x00000000), v116 = unchecked((uint)0x00000000), v117 = unchecked((uint)0x00000000), v119 = unchecked((uint)0x00000000), v120 = unchecked((uint)0x00000000), v121 = unchecked((uint)0x00000000), v123 = unchecked((uint)0x00000000), v124 = unchecked((uint)0x00000000);
            int v6 = unchecked((int)0x00000000), v8 = unchecked((int)0x00000000), v10 = unchecked((int)0x00000000), v12 = unchecked((int)0x00000000), v15 = unchecked((int)0x00000000), v17 = unchecked((int)0x00000000), v21 = unchecked((int)0x00000000), v26 = unchecked((int)0x00000000), v30 = unchecked((int)0x00000000), v34 = unchecked((int)0x00000000), v36 = unchecked((int)0x00000000), v38 = unchecked((int)0x00000000), v45 = unchecked((int)0x00000000), v46 = unchecked((int)0x00000000), v48 = unchecked((int)0x00000000), v53 = unchecked((int)0x00000000), v59 = unchecked((int)0x00000000), v60 = unchecked((int)0x00000000), v64 = unchecked((int)0x00000000), v70 = unchecked((int)0x00000000), v76 = unchecked((int)0x00000000), v77 = unchecked((int)0x00000000), v79 = unchecked((int)0x00000000), v81 = unchecked((int)0x00000000), v90 = unchecked((int)0x00000000), v92 = unchecked((int)0x00000000), v95 = unchecked((int)0x00000000), v98 = unchecked((int)0x00000000), v106 = unchecked((int)0x00000000), v110 = unchecked((int)0x00000000), v118 = unchecked((int)0x00000000), v122 = unchecked((int)0x00000000);
            b0:
                __trace[__traceLen++] = 0;
                v1 = (unchecked((uint)0x00000000) - p1);
                v2 = ~p1;
                v3 = unchecked((uint)0x0003B742) % p1;
                v4 = unchecked((uint)0x000EBA68) - unchecked((uint)0x00020256);
                v5 = v4 + unchecked((uint)0xFFF392DE);
                v6 = (unchecked((uint)0x00000000) < p0) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                if (v6 != unchecked((int)0x00000000)) goto b1;
                goto b3;
            b1:
                __trace[__traceLen++] = 1;
                v7 = unchecked((uint)0x000003E8) * unchecked((uint)0x000003E8);
                v8 = BitOperations.PopCount(unchecked((uint)0xFFFFFFFE));
                v9 = (uint)(v8);
                v10 = (BitOperations.IsPow2(v7) ? 1 : 0);
                v11 = (uint)(v10);
                v12 = (unchecked((uint)0x00035FCF) > p1) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                v13 = (uint)(v12);
                v14 = v13 - unchecked((uint)0x000D3403);
                v15 = (unchecked((uint)0xFFFFFC18) < unchecked((uint)0x000E4C4C)) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                v16 = (uint)(v15);
                v17 = (v14 > v4) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                if (v17 != unchecked((int)0x00000000)) goto b2;
                goto b4;
            b2:
                __trace[__traceLen++] = 2;
                v18 = unchecked((uint)0xFFF14FB8) - v7;
                v19 = v5 % p0;
                v20 = unchecked((uint)0x00000007) * v4;
                v21 = (p0 <= v11) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                if (v21 != unchecked((int)0x00000000)) goto b6;
                goto b11;
            b3:
                __trace[__traceLen++] = 3;
                v22 = v14 - unchecked((uint)0x000CBA13);
                v23 = v11 | unchecked((uint)0xFFFC3D1A);
                v24 = v4 & v22;
                v25 = unchecked((uint)0x00000000) & v1;
                v26 = (v24 > p0) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                if (v26 != unchecked((int)0x00000000)) goto b10;
                goto b12;
            b4:
                __trace[__traceLen++] = 4;
                v27 = ~v1;
                v28 = v23 & v23;
                v29 = v13 + v2;
                v30 = (v25 >= unchecked((uint)0xFFF454BF)) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                v31 = (uint)(v30);
                v32 = unchecked((uint)0xFFFAFC10) * unchecked((uint)0x0009156D);
                goto b5;
            b5:
                __trace[__traceLen++] = 5;
                v33 = BitOperations.RotateLeft(unchecked((uint)0x00000001), (int)(unchecked((uint)0x00000003)));
                v34 = (BitOperations.IsPow2(unchecked((uint)0x00088E9E)) ? 1 : 0);
                v35 = (uint)(v34);
                v36 = (v20 < v3) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                v37 = (uint)(v36);
                v38 = (v4 > v37) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                if (v38 != unchecked((int)0x00000000)) goto b9;
                goto b17;
            b6:
                __trace[__traceLen++] = 6;
                v39 = v28 - v2;
                v40 = v4 - v13;
                v41 = ~v29;
                v42 = unchecked((uint)0x0006EE48) & unchecked((uint)0x0007BA65);
                v43 = unchecked((uint)0xFFF9D5DE) - v29;
                v44 = ~v3;
                v45 = (v42 <= v44) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                if (v45 != unchecked((int)0x00000000)) goto b7;
                goto b8;
            b7:
                __trace[__traceLen++] = 7;
                v46 = (BitOperations.IsPow2(unchecked((uint)0x00000001)) ? 1 : 0);
                v47 = (uint)(v46);
                v48 = (v9 >= unchecked((uint)0xFFFC9099)) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                v49 = (uint)(v48);
                v50 = Math.Clamp(unchecked((uint)0xFFF8528F), unchecked((uint)0x00000005), v47);
                v51 = v5 | v1;
                v52 = v14 & unchecked((uint)0x000E9218);
                v53 = (v2 >= v3) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                if (v53 != unchecked((int)0x00000000)) goto b14;
                goto b11;
            b8:
                __trace[__traceLen++] = 8;
                v54 = v7 + unchecked((uint)0xFFF26B1D);
                v55 = v25 % unchecked((uint)0x00012C0B);
                v56 = v43 + v43;
                v57 = ~v29;
                v58 = v52 * v3;
                v59 = (v52 == unchecked((uint)0x0000000D)) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                if (v59 != unchecked((int)0x00000000)) goto b19;
                goto b13;
            b9:
                __trace[__traceLen++] = 9;
                v60 = (v4 < v1) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                v61 = (uint)(v60);
                v62 = (unchecked((uint)0x00000000) - v24);
                v63 = v9 - v22;
                v64 = BitOperations.TrailingZeroCount(unchecked((uint)0xFFFFFFFE));
                v65 = (uint)(v64);
                return v65;
            b10:
                __trace[__traceLen++] = 10;
                v66 = v51 >>> (int)(v61);
                v67 = v58 - unchecked((uint)0x0004A8B9);
                v68 = Math.Max(unchecked((uint)0xFFFA3E09), unchecked((uint)0xFFFFFFFD));
                v69 = v33 - v50;
                v70 = BitOperations.PopCount(unchecked((uint)0xFFFBC8AA));
                v71 = (uint)(v70);
                v72 = v4 | unchecked((uint)0x00004D11);
                goto b18;
            b11:
                __trace[__traceLen++] = 11;
                v73 = v51 + unchecked((uint)0x000003E8);
                v74 = BitOperations.RotateRight(v39, (int)(v42));
                v75 = unchecked((uint)0xFFFAF3CA) + v58;
                v76 = (v68 == v19) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                if (v76 != unchecked((int)0x00000000)) goto b13;
                goto b0;
            b12:
                __trace[__traceLen++] = 12;
                v77 = (v4 < v23) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                v78 = (uint)(v77);
                v79 = (v35 <= unchecked((uint)0x000B9492)) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                v80 = (uint)(v79);
                v81 = BitOperations.TrailingZeroCount(unchecked((uint)0xFFFFFFFF));
                v82 = (uint)(v81);
                return v82;
            b13:
                __trace[__traceLen++] = 13;
                v83 = ~v23;
                v84 = v25 + v1;
                v85 = Math.Max(unchecked((uint)0x80000000), p1);
                v86 = unchecked((uint)0xFFFF5264) % v84;
                v87 = p0 & unchecked((uint)0xFFFFFFF9);
                v88 = ~p0;
                goto b2;
            b14:
                __trace[__traceLen++] = 14;
                v89 = v24 | v50;
                v90 = (v75 != v67) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                v91 = (uint)(v90);
                v92 = (unchecked((uint)0xFFFE36FE) >= unchecked((uint)0xFFFC820F)) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                v93 = (uint)(v92);
                v94 = v68 + unchecked((uint)0xFFFFE835);
                v95 = (v5 != v47) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                v96 = (uint)(v95);
                v97 = v56 | unchecked((uint)0xFFF13A97);
                v98 = (v41 < v73) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                if (v98 != unchecked((int)0x00000000)) goto b15;
                goto b16;
            b15:
                __trace[__traceLen++] = 15;
                v99 = (unchecked((uint)0x00000000) - v44);
                v100 = (unchecked((uint)0x00000000) - v54);
                v101 = unchecked((uint)0x0008CF57) ^ v78;
                v102 = v93 >> (int)(unchecked((uint)0xFFFEE4CC));
                v103 = unchecked((uint)0x0000000B) + unchecked((uint)0xFFF57D10);
                goto b18;
            b16:
                __trace[__traceLen++] = 16;
                v104 = p1 & unchecked((uint)0xFFF40D10);
                v105 = unchecked((uint)0x0000000B) % unchecked((uint)0x0000A288);
                v106 = (v67 < unchecked((uint)0xFFF41EEF)) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                v107 = (uint)(v106);
                return v107;
            b17:
                __trace[__traceLen++] = 17;
                v108 = v11 >> (int)(v23);
                v109 = v75 >> (int)(v41);
                v110 = (v1 < v37) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                v111 = (uint)(v110);
                v112 = v62 + v65;
                return v112;
            b18:
                __trace[__traceLen++] = 18;
                v113 = v102 | unchecked((uint)0xFFF905A6);
                v114 = Math.Clamp(unchecked((uint)0x00000001), unchecked((uint)0x00000001), unchecked((uint)0x00000000));
                v115 = unchecked((uint)0x7FFFFFFE) + unchecked((uint)0x00002928);
                v116 = v7 - unchecked((uint)0xFFFA72BA);
                v117 = v115 - v32;
                return v117;
            b19:
                __trace[__traceLen++] = 19;
                v118 = (v83 <= unchecked((uint)0x000BF458)) ? unchecked((int)0x00000001) : unchecked((int)0x00000000);
                v119 = (uint)(v118);
                v120 = unchecked((uint)0x0000001F) * v93;
                v121 = ~v75;
                v122 = BitOperations.Log2(v24);
                v123 = (uint)(v122);
                v124 = v63 + v3;
                return v124;
        }
    }

    public static int Main()
    {
        bool __doWarmup = System.Environment.GetEnvironmentVariable("REIFYCS_SKIP_WARMUP") == null;
        if (__doWarmup)
        {
            for (int __w = 0; __w < 200; __w++)
            {
                Sink = Fn30000002142(Input_p0, Input_p1);
                __traceLen = 0;  // discard warmup traces
            }
            System.Threading.Thread.Sleep(75);  // Tier0 -> Instrumented Tier0
            for (int __s = 0; __s < 100; __s++)
            {
                Sink = Fn30000002142(Input_p0, Input_p1);
                __traceLen = 0;  // discard settle traces
            }
            System.Threading.Thread.Sleep(125);  // Instrumented Tier0 -> Tier1
        }
        uint result = Fn30000002142(Input_p0, Input_p1);
        Console.WriteLine($"{(uint)result:X8}");
        var sb = new System.Text.StringBuilder();
        for (int i = 0; i < __traceLen; i++) {
            if (i > 0) sb.Append(' ');
            sb.Append(__trace[i]);
        }
        Console.WriteLine("TRACE " + sb.ToString());
        return 0;
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIuntriagedNew issue has not been triaged by the area owner

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions