diff --git a/src/coreclr/vm/amd64/JitHelpers_InlineGetThread.asm b/src/coreclr/vm/amd64/JitHelpers_InlineGetThread.asm index 5e93265cf54873db7cc2fb3d3a12c01d2ba8e705..bf79668e567e29bdcdeacd5052c2582d82ce6cd7 100644 --- a/src/coreclr/vm/amd64/JitHelpers_InlineGetThread.asm +++ b/src/coreclr/vm/amd64/JitHelpers_InlineGetThread.asm @@ -60,15 +60,9 @@ LEAF_END JIT_TrialAllocSFastMP_InlineGetThread, _TEXT ; HCIMPL2(Object*, JIT_Box, CORINFO_CLASS_HANDLE type, void* unboxedData) NESTED_ENTRY JIT_BoxFastMP_InlineGetThread, _TEXT - mov rax, [rcx + OFFSETOF__MethodTable__m_pWriteableData] - - ; Check whether the class has not been initialized - test dword ptr [rax + OFFSETOF__MethodTableWriteableData__m_dwFlags], MethodTableWriteableData__enum_flag_Unrestored - jnz ClassNotInited - - mov r8d, [rcx + OFFSET__MethodTable__m_BaseSize] ; m_BaseSize is guaranteed to be a multiple of 8. + mov r8d, [rcx + OFFSET__MethodTable__m_BaseSize] INLINE_GETTHREAD r11 mov r10, [r11 + OFFSET__Thread__m_alloc_context__alloc_limit] @@ -79,6 +73,9 @@ NESTED_ENTRY JIT_BoxFastMP_InlineGetThread, _TEXT cmp r8, r10 ja AllocFailed + test rdx, rdx + je NullRef + mov [r11 + OFFSET__Thread__m_alloc_context__alloc_ptr], r8 mov [rax], rcx @@ -113,8 +110,8 @@ align 16 pop rax ret - ClassNotInited: AllocFailed: + NullRef: jmp JIT_Box NESTED_END JIT_BoxFastMP_InlineGetThread, _TEXT diff --git a/src/coreclr/vm/amd64/JitHelpers_Slow.asm b/src/coreclr/vm/amd64/JitHelpers_Slow.asm index 2efd0b122488cd53ff9fdcc8c373ffd7f36ff741..50fe4c64b66f9f1435185b3de912dc18099ce618 100644 --- a/src/coreclr/vm/amd64/JitHelpers_Slow.asm +++ b/src/coreclr/vm/amd64/JitHelpers_Slow.asm @@ -205,15 +205,8 @@ LEAF_END JIT_TrialAllocSFastSP, _TEXT ; HCIMPL2(Object*, JIT_Box, CORINFO_CLASS_HANDLE type, void* unboxedData) NESTED_ENTRY JIT_BoxFastUP, _TEXT - mov rax, [rcx + OFFSETOF__MethodTable__m_pWriteableData] - - ; Check whether the class has not been initialized - test dword ptr [rax + OFFSETOF__MethodTableWriteableData__m_dwFlags], MethodTableWriteableData__enum_flag_Unrestored - jnz JIT_Box - - mov r8d, [rcx + OFFSET__MethodTable__m_BaseSize] - ; m_BaseSize is guaranteed to be a multiple of 8. + mov r8d, [rcx + OFFSET__MethodTable__m_BaseSize] inc [g_global_alloc_lock] jnz JIT_Box @@ -226,6 +219,8 @@ NESTED_ENTRY JIT_BoxFastUP, _TEXT cmp r8, r10 ja NoAlloc + test rdx, rdx + je NullRef mov qword ptr [g_global_alloc_context + OFFSETOF__gc_alloc_context__alloc_ptr], r8 ; update the alloc ptr mov [rax], rcx @@ -265,6 +260,7 @@ NESTED_ENTRY JIT_BoxFastUP, _TEXT ret NoAlloc: + NullRef: mov [g_global_alloc_lock], -1 jmp JIT_Box NESTED_END JIT_BoxFastUP, _TEXT diff --git a/src/coreclr/vm/amd64/asmconstants.h b/src/coreclr/vm/amd64/asmconstants.h index 2afddae98a4d3f0b809d9cea46ba5b287ebda9bd..e578f21ee7a02affdf3c1d451cef72874f2c5a03 100644 --- a/src/coreclr/vm/amd64/asmconstants.h +++ b/src/coreclr/vm/amd64/asmconstants.h @@ -163,10 +163,6 @@ ASMCONSTANTS_C_ASSERT(OFFSETOF__MethodTable__m_wNumInterfaces ASMCONSTANTS_C_ASSERT(OFFSETOF__MethodTable__m_pParentMethodTable == offsetof(MethodTable, m_pParentMethodTable)); -#define OFFSETOF__MethodTable__m_pWriteableData DBG_FRE(0x28, 0x20) -ASMCONSTANTS_C_ASSERT(OFFSETOF__MethodTable__m_pWriteableData - == offsetof(MethodTable, m_pWriteableData)); - #define OFFSETOF__MethodTable__m_pEEClass DBG_FRE(0x30, 0x28) ASMCONSTANTS_C_ASSERT(OFFSETOF__MethodTable__m_pEEClass == offsetof(MethodTable, m_pEEClass)); @@ -201,14 +197,6 @@ ASMCONSTANTS_C_ASSERT(METHODTABLE_EQUIVALENCE_FLAGS ASMCONSTANTS_C_ASSERT(MethodTable__enum_flag_ContainsPointers == MethodTable::enum_flag_ContainsPointers); -#define OFFSETOF__MethodTableWriteableData__m_dwFlags 0 -ASMCONSTANTS_C_ASSERT(OFFSETOF__MethodTableWriteableData__m_dwFlags - == offsetof(MethodTableWriteableData, m_dwFlags)); - -#define MethodTableWriteableData__enum_flag_Unrestored 0x04 -ASMCONSTANTS_C_ASSERT(MethodTableWriteableData__enum_flag_Unrestored - == MethodTableWriteableData::enum_flag_Unrestored); - #define OFFSETOF__InterfaceInfo_t__m_pMethodTable 0 ASMCONSTANTS_C_ASSERT(OFFSETOF__InterfaceInfo_t__m_pMethodTable == offsetof(InterfaceInfo_t, m_pMethodTable)); diff --git a/src/coreclr/vm/arm/asmconstants.h b/src/coreclr/vm/arm/asmconstants.h index 8d8a1f4f0ea0ecd49370b21711451342ab61a160..277afad56e763cee027a766d14829e657b6abaa3 100644 --- a/src/coreclr/vm/arm/asmconstants.h +++ b/src/coreclr/vm/arm/asmconstants.h @@ -76,9 +76,6 @@ ASMCONSTANTS_C_ASSERT(MethodTable__m_BaseSize == offsetof(MethodTable, m_BaseSiz #define MethodTable__m_dwFlags 0x0 ASMCONSTANTS_C_ASSERT(MethodTable__m_dwFlags == offsetof(MethodTable, m_dwFlags)); -#define MethodTable__m_pWriteableData DBG_FRE(0x1c, 0x18) -ASMCONSTANTS_C_ASSERT(MethodTable__m_pWriteableData == offsetof(MethodTable, m_pWriteableData)); - #define MethodTable__enum_flag_ContainsPointers 0x01000000 ASMCONSTANTS_C_ASSERT(MethodTable__enum_flag_ContainsPointers == MethodTable::enum_flag_ContainsPointers); @@ -88,12 +85,6 @@ ASMCONSTANTS_C_ASSERT(MethodTable__m_ElementType == offsetof(MethodTable, m_pMul #define SIZEOF__MethodTable DBG_FRE(0x2c, 0x28) ASMCONSTANTS_C_ASSERT(SIZEOF__MethodTable == sizeof(MethodTable)); -#define MethodTableWriteableData__m_dwFlags 0x00 -ASMCONSTANTS_C_ASSERT(MethodTableWriteableData__m_dwFlags == offsetof(MethodTableWriteableData, m_dwFlags)); - -#define MethodTableWriteableData__enum_flag_Unrestored 0x04 -ASMCONSTANTS_C_ASSERT(MethodTableWriteableData__enum_flag_Unrestored == MethodTableWriteableData::enum_flag_Unrestored); - #define ArrayBase__m_NumComponents 0x4 ASMCONSTANTS_C_ASSERT(ArrayBase__m_NumComponents == offsetof(ArrayBase, m_NumComponents)); diff --git a/src/coreclr/vm/i386/gmsx86.cpp b/src/coreclr/vm/i386/gmsx86.cpp index 9d982ad689a2eecd44a6c85b73c3ffe49437ad32..5bb4cb8c821b957904caedd91a873d49fdc0a6cb 100644 --- a/src/coreclr/vm/i386/gmsx86.cpp +++ b/src/coreclr/vm/i386/gmsx86.cpp @@ -897,6 +897,7 @@ void LazyMachState::unwindLazyState(LazyMachState* baseState, case 0x03: case 0x11: // ADC mod/rm case 0x13: + case 0x21: // AND mod/rm case 0x29: // SUB mod/rm case 0x2B: datasize = 0; diff --git a/src/coreclr/vm/i386/jitinterfacex86.cpp b/src/coreclr/vm/i386/jitinterfacex86.cpp index 641925821ac67ec277f08d0e6a4086bcb90bb0ca..82782d86c2bf621d7e01c55dcd21bc14f7696c1b 100644 --- a/src/coreclr/vm/i386/jitinterfacex86.cpp +++ b/src/coreclr/vm/i386/jitinterfacex86.cpp @@ -420,27 +420,18 @@ void *JIT_TrialAlloc::GenBox(Flags flags) CodeLabel *noLock = sl.NewCodeLabel(); CodeLabel *noAlloc = sl.NewCodeLabel(); + CodeLabel *nullRef = sl.NewCodeLabel(); // Save address of value to be boxed sl.X86EmitPushReg(kEBX); sl.Emit16(0xda8b); - // Save the MethodTable ptr - sl.X86EmitPushReg(kECX); - - // mov ecx, [ecx]MethodTable.m_pWriteableData - sl.X86EmitOffsetModRM(0x8b, kECX, kECX, offsetof(MethodTable, m_pWriteableData)); - - // Check whether the class has not been initialized - // test [ecx]MethodTableWriteableData.m_dwFlags,MethodTableWriteableData::enum_flag_Unrestored - sl.X86EmitOffsetModRM(0xf7, (X86Reg)0x0, kECX, offsetof(MethodTableWriteableData, m_dwFlags)); - sl.Emit32(MethodTableWriteableData::enum_flag_Unrestored); + // Check for null ref + // test edx, edx + sl.X86EmitR2ROp(0x85, kEDX, kEDX); - // Restore the MethodTable ptr in ecx - sl.X86EmitPopReg(kECX); - - // jne noAlloc - sl.X86EmitCondJump(noAlloc, X86CondCode::kJNE); + // je nullRef + sl.X86EmitCondJump(nullRef, X86CondCode::kJE); // Emit the main body of the trial allocator EmitCore(&sl, noLock, noAlloc, flags); @@ -527,8 +518,9 @@ void *JIT_TrialAlloc::GenBox(Flags flags) sl.X86EmitReturn(0); - // Come here in case of no space + // Come here in case of no space or null ref sl.EmitLabel(noAlloc); + sl.EmitLabel(nullRef); // Release the lock in the uniprocessor case EmitNoAllocCode(&sl, flags); diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 0e2cbd76b566ca264edcff6099a6e90af511a869..44ec15481fa2f4141d1ada96204d7da17920385c 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -2691,6 +2691,10 @@ HCIMPL2(Object*, JIT_Box, CORINFO_CLASS_HANDLE type, void* unboxedData) GCPROTECT_BEGININTERIOR(unboxedData); HELPER_METHOD_POLL(); + // A null can be passed for boxing of a null ref. + if (unboxedData == NULL) + COMPlusThrow(kNullReferenceException); + TypeHandle clsHnd(type); _ASSERTE(!clsHnd.IsTypeDesc()); // boxable types have method tables diff --git a/src/mono/mono/metadata/threads.c b/src/mono/mono/metadata/threads.c index 378cec13b1efe02ae6c3f35105ba5caa96541cd1..de31cb1a120dc44b7a0be08448b81ecc0248ea7a 100644 --- a/src/mono/mono/metadata/threads.c +++ b/src/mono/mono/metadata/threads.c @@ -2060,13 +2060,30 @@ ves_icall_System_Threading_Thread_Join_internal (MonoThreadObjectHandle thread_h return FALSE; } +// this is a bad idea but we're doing it anyway. we need to propagate +// an exception out of these icalls in some way. +static size_t +set_pending_null_reference_exception (void) +{ + ERROR_DECL (error); + mono_error_set_null_reference (error); + mono_error_set_pending_exception (error); + return 0; +} + gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location) { + if (G_UNLIKELY (!location)) + return (gint32)set_pending_null_reference_exception (); + return mono_atomic_inc_i32 (location); } gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location) { + if (G_UNLIKELY (!location)) + return (gint64)set_pending_null_reference_exception (); + #if SIZEOF_VOID_P == 4 if (G_UNLIKELY ((size_t)location & 0x7)) { gint64 ret; @@ -2082,11 +2099,17 @@ gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location) gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location) { + if (G_UNLIKELY (!location)) + return (gint32)set_pending_null_reference_exception (); + return mono_atomic_dec_i32(location); } gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location) { + if (G_UNLIKELY (!location)) + return (gint64)set_pending_null_reference_exception (); + #if SIZEOF_VOID_P == 4 if (G_UNLIKELY ((size_t)location & 0x7)) { gint64 ret; @@ -2102,12 +2125,21 @@ gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location) gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location, gint32 value) { + if (G_UNLIKELY (!location)) + return (gint32)set_pending_null_reference_exception (); + return mono_atomic_xchg_i32(location, value); } void ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject *volatile*location, MonoObject *volatile*value, MonoObject *volatile*res) { + if (G_UNLIKELY (!location)) + { + (void)set_pending_null_reference_exception (); + return; + } + // Coop-equivalency here via pointers to pointers. // value and res are to managed frames, location ought to be (or member or global) but it cannot be guaranteed. // @@ -2123,6 +2155,8 @@ ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject *volatile*loc gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location, gfloat value) { IntFloatUnion val, ret; + if (G_UNLIKELY (!location)) + return (gfloat)set_pending_null_reference_exception (); val.fval = value; ret.ival = mono_atomic_xchg_i32((gint32 *) location, val.ival); @@ -2133,6 +2167,9 @@ gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location, gint64 ves_icall_System_Threading_Interlocked_Exchange_Long (gint64 *location, gint64 value) { + if (G_UNLIKELY (!location)) + return (gint64)set_pending_null_reference_exception (); + #if SIZEOF_VOID_P == 4 if (G_UNLIKELY ((size_t)location & 0x7)) { gint64 ret; @@ -2150,6 +2187,8 @@ gdouble ves_icall_System_Threading_Interlocked_Exchange_Double (gdouble *location, gdouble value) { LongDoubleUnion val, ret; + if (G_UNLIKELY (!location)) + return (gdouble)set_pending_null_reference_exception (); val.fval = value; ret.ival = (gint64)mono_atomic_xchg_i64((gint64 *) location, val.ival); @@ -2159,11 +2198,17 @@ ves_icall_System_Threading_Interlocked_Exchange_Double (gdouble *location, gdoub gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location, gint32 value, gint32 comparand) { + if (G_UNLIKELY (!location)) + return (gint32)set_pending_null_reference_exception (); + return mono_atomic_cas_i32(location, value, comparand); } gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int_Success(gint32 *location, gint32 value, gint32 comparand, MonoBoolean *success) { + if (G_UNLIKELY (!location)) + return (gint32)set_pending_null_reference_exception (); + gint32 r = mono_atomic_cas_i32(location, value, comparand); *success = r == comparand; return r; @@ -2172,6 +2217,12 @@ gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int_Success(gint32 void ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject *volatile*location, MonoObject *volatile*value, MonoObject *volatile*comparand, MonoObject *volatile* res) { + if (G_UNLIKELY (!location)) + { + (void)set_pending_null_reference_exception (); + return; + } + // Coop-equivalency here via pointers to pointers. // value and comparand and res are to managed frames, location ought to be (or member or global) but it cannot be guaranteed. // @@ -2187,6 +2238,8 @@ ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject *volat gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location, gfloat value, gfloat comparand) { IntFloatUnion val, ret, cmp; + if (G_UNLIKELY (!location)) + return (gfloat)set_pending_null_reference_exception (); val.fval = value; cmp.fval = comparand; @@ -2198,6 +2251,9 @@ gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *lo gdouble ves_icall_System_Threading_Interlocked_CompareExchange_Double (gdouble *location, gdouble value, gdouble comparand) { + if (G_UNLIKELY (!location)) + return (gdouble)set_pending_null_reference_exception (); + #if SIZEOF_VOID_P == 8 LongDoubleUnion val, comp, ret; @@ -2222,6 +2278,9 @@ ves_icall_System_Threading_Interlocked_CompareExchange_Double (gdouble *location gint64 ves_icall_System_Threading_Interlocked_CompareExchange_Long (gint64 *location, gint64 value, gint64 comparand) { + if (G_UNLIKELY (!location)) + return (gint64)set_pending_null_reference_exception (); + #if SIZEOF_VOID_P == 4 if (G_UNLIKELY ((size_t)location & 0x7)) { gint64 old; @@ -2239,12 +2298,18 @@ ves_icall_System_Threading_Interlocked_CompareExchange_Long (gint64 *location, g gint32 ves_icall_System_Threading_Interlocked_Add_Int (gint32 *location, gint32 value) { + if (G_UNLIKELY (!location)) + return (gint32)set_pending_null_reference_exception (); + return mono_atomic_add_i32 (location, value); } gint64 ves_icall_System_Threading_Interlocked_Add_Long (gint64 *location, gint64 value) { + if (G_UNLIKELY (!location)) + return (gint64)set_pending_null_reference_exception (); + #if SIZEOF_VOID_P == 4 if (G_UNLIKELY ((size_t)location & 0x7)) { gint64 ret; @@ -2261,6 +2326,9 @@ ves_icall_System_Threading_Interlocked_Add_Long (gint64 *location, gint64 value) gint64 ves_icall_System_Threading_Interlocked_Read_Long (gint64 *location) { + if (G_UNLIKELY (!location)) + return (gint64)set_pending_null_reference_exception (); + #if SIZEOF_VOID_P == 4 if (G_UNLIKELY ((size_t)location & 0x7)) { gint64 ret; diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 5e2907780faa99199c5e2f078638f8e76cfeca54..ed3a579523e5afa1e6371c891d706c2d95397f01 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -6681,6 +6681,7 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK; gboolean flag = FALSE; gint64 *dest = LOCAL_VAR (ip [2], gint64*); gint64 exch = LOCAL_VAR (ip [3], gint64); + NULL_CHECK(dest); #if SIZEOF_VOID_P == 4 if (G_UNLIKELY (((size_t)dest) & 0x7)) { gint64 result; diff --git a/src/mono/mono/mini/intrinsics.c b/src/mono/mono/mini/intrinsics.c index 6aecbff96dd1c8108db2e5f43901556c9a3874bc..17b78eb529462172b5ad80d78e37c2437e6a8a31 100644 --- a/src/mono/mono/mini/intrinsics.c +++ b/src/mono/mono/mini/intrinsics.c @@ -1238,6 +1238,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign ins_iconst->dreg = mono_alloc_ireg (cfg); MONO_ADD_INS (cfg->cbb, ins_iconst); + MONO_EMIT_NULL_CHECK (cfg, args [0]->dreg, FALSE); MONO_INST_NEW (cfg, ins, opcode); ins->dreg = mono_alloc_ireg (cfg); ins->inst_basereg = args [0]->dreg; @@ -1266,6 +1267,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign ins_iconst->dreg = mono_alloc_ireg (cfg); MONO_ADD_INS (cfg->cbb, ins_iconst); + MONO_EMIT_NULL_CHECK (cfg, args [0]->dreg, FALSE); MONO_INST_NEW (cfg, ins, opcode); ins->dreg = mono_alloc_ireg (cfg); ins->inst_basereg = args [0]->dreg; @@ -1304,6 +1306,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign // For now, only Add is supported in non-LLVM back-ends if (opcode && (COMPILE_LLVM (cfg) || mono_arch_opcode_supported (opcode))) { + MONO_EMIT_NULL_CHECK (cfg, args [0]->dreg, FALSE); MONO_INST_NEW (cfg, ins, opcode); ins->dreg = mono_alloc_ireg (cfg); ins->inst_basereg = args [0]->dreg; @@ -1363,6 +1366,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign if (is_ref && !mini_debug_options.weak_memory_model) mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL); + MONO_EMIT_NULL_CHECK (cfg, args [0]->dreg, FALSE); MONO_INST_NEW (cfg, ins, opcode); ins->dreg = is_ref ? mono_alloc_ireg_ref (cfg) : mono_alloc_ireg (cfg); ins->inst_basereg = args [0]->dreg; @@ -1466,6 +1470,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign if (is_ref && !mini_debug_options.weak_memory_model) mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL); + MONO_EMIT_NULL_CHECK (cfg, args [0]->dreg, FALSE); MONO_INST_NEW (cfg, ins, opcode); ins->dreg = is_ref ? alloc_ireg_ref (cfg) : alloc_ireg (cfg); ins->sreg1 = args [0]->dreg; diff --git a/src/tests/baseservices/invalid_operations/InvalidOperations.csproj b/src/tests/baseservices/invalid_operations/InvalidOperations.csproj new file mode 100644 index 0000000000000000000000000000000000000000..3c04f092ae83ca3d48380b9f8b5778a74ed0d30b --- /dev/null +++ b/src/tests/baseservices/invalid_operations/InvalidOperations.csproj @@ -0,0 +1,12 @@ + + + Exe + true + + + + + + + + \ No newline at end of file diff --git a/src/tests/baseservices/invalid_operations/ManagedPointers.cs b/src/tests/baseservices/invalid_operations/ManagedPointers.cs new file mode 100644 index 0000000000000000000000000000000000000000..c5357cf03541e6b3f867e279883cd992215b1856 --- /dev/null +++ b/src/tests/baseservices/invalid_operations/ManagedPointers.cs @@ -0,0 +1,84 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; + +using Xunit; + +public unsafe class ManagedPointers +{ + [Fact] + public static void Validate_BoxingHelpers_NullByRef() + { + Console.WriteLine($"Running {nameof(Validate_BoxingHelpers_NullByRef)}..."); + Assert.Throws(() => + { + object boxed = Unsafe.NullRef(); + }); + Assert.Throws(() => + { + object boxed = Unsafe.NullRef(); + }); + Assert.Throws(() => + { + object boxed = Unsafe.NullRef(); + }); + } + + [Fact] + public static void Validate_GeneratedILStubs_NullByRef() + { + Console.WriteLine($"Running {nameof(Validate_GeneratedILStubs_NullByRef)}..."); + { + var fptr = (delegate*unmanaged)(delegate*unmanaged)&PassByRef; + Assert.Equal(0, fptr(ref Unsafe.NullRef())); + } + + { + var fptr = (delegate*unmanaged)(delegate*unmanaged)&PassByRef; + Assert.Equal(0, fptr(ref Unsafe.NullRef())); + } + + Assert.Throws(() => + { + var fptr = (delegate*unmanaged)(delegate*unmanaged)&PassByRef; + fptr(ref Unsafe.NullRef()); + }); + + [UnmanagedCallersOnly] + static nint PassByRef(void* a) => (nint)a; + } + + [Fact] + public static void Validate_IntrinsicMethodsWithByRef_NullByRef() + { + Console.WriteLine($"Running {nameof(Validate_IntrinsicMethodsWithByRef_NullByRef)}..."); + + Assert.Throws(() => Interlocked.Increment(ref Unsafe.NullRef())); + Assert.Throws(() => Interlocked.Increment(ref Unsafe.NullRef())); + Assert.Throws(() => Interlocked.Decrement(ref Unsafe.NullRef())); + Assert.Throws(() => Interlocked.Decrement(ref Unsafe.NullRef())); + + Assert.Throws(() => Interlocked.And(ref Unsafe.NullRef(), 0)); + Assert.Throws(() => Interlocked.And(ref Unsafe.NullRef(), 0)); + Assert.Throws(() => Interlocked.Or(ref Unsafe.NullRef(), 0)); + Assert.Throws(() => Interlocked.Or(ref Unsafe.NullRef(), 0)); + + Assert.Throws(() => Interlocked.Exchange(ref Unsafe.NullRef(), 0)); + Assert.Throws(() => Interlocked.Exchange(ref Unsafe.NullRef(), 0)); + Assert.Throws(() => Interlocked.Exchange(ref Unsafe.NullRef(), 0)); + Assert.Throws(() => Interlocked.Exchange(ref Unsafe.NullRef(), 0)); + Assert.Throws(() => Interlocked.Exchange(ref Unsafe.NullRef(), new object())); + Assert.Throws(() => Interlocked.Exchange(ref Unsafe.NullRef(), new object())); + + Assert.Throws(() => Interlocked.CompareExchange(ref Unsafe.NullRef(), 0, 0)); + Assert.Throws(() => Interlocked.CompareExchange(ref Unsafe.NullRef(), 0, 0)); + Assert.Throws(() => Interlocked.CompareExchange(ref Unsafe.NullRef(), 0, 0)); + Assert.Throws(() => Interlocked.CompareExchange(ref Unsafe.NullRef(), 0, 0)); + Assert.Throws(() => Interlocked.CompareExchange(ref Unsafe.NullRef(), new object(), new object())); + Assert.Throws(() => Interlocked.CompareExchange(ref Unsafe.NullRef(), new object(), new object())); + } +} \ No newline at end of file diff --git a/src/tests/issues.targets b/src/tests/issues.targets index beb49a9d74d9fa5f764d8ed84b6f50ed298c351b..05214a73a43097253ff66951ac91fdd598f60a71 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -3114,6 +3114,9 @@ Doesn't compile with LLVM AOT. + + Doesn't compile with LLVM AOT. + @@ -3290,6 +3293,9 @@ https://github.com/dotnet/runtime/issues/54122 + + Function mismatch + https://github.com/dotnet/runtime/issues/41472