From a5501cb00edbcdc4c0d4d05f089daa9b4b4c4da8 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Mon, 9 Sep 2013 17:15:39 +0200 Subject: [PATCH] [runtime] Fix a nullref when casting a null object to a complex type when using --debug=casts. Fixes #14552/#14493. --- mono/mini/method-to-ir.c | 32 ++++++++++++++++++++++++-------- mono/tests/debug-casts.cs | 12 ++++++++++++ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/mono/mini/method-to-ir.c b/mono/mini/method-to-ir.c index 4457c47c9d313..a4fc0a37da12a 100644 --- a/mono/mini/method-to-ir.c +++ b/mono/mini/method-to-ir.c @@ -3339,14 +3339,23 @@ emit_seq_point (MonoCompile *cfg, MonoMethod *method, guint8* ip, gboolean intr_ } static void -save_cast_details (MonoCompile *cfg, MonoClass *klass, int obj_reg) +save_cast_details (MonoCompile *cfg, MonoClass *klass, int obj_reg, gboolean null_check, MonoBasicBlock **out_bblock) { if (mini_get_debug_options ()->better_cast_details) { int to_klass_reg = alloc_preg (cfg); int vtable_reg = alloc_preg (cfg); int klass_reg = alloc_preg (cfg); - MonoInst *tls_get = mono_get_jit_tls_intrinsic (cfg); + MonoBasicBlock *is_null_bb = NULL; + MonoInst *tls_get; + + if (null_check) { + NEW_BBLOCK (cfg, is_null_bb); + + MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0); + MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb); + } + tls_get = mono_get_jit_tls_intrinsic (cfg); if (!tls_get) { fprintf (stderr, "error: --debug=casts not supported on this platform.\n."); exit (1); @@ -3359,6 +3368,12 @@ save_cast_details (MonoCompile *cfg, MonoClass *klass, int obj_reg) MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, tls_get->dreg, G_STRUCT_OFFSET (MonoJitTlsData, class_cast_from), klass_reg); MONO_EMIT_NEW_PCONST (cfg, to_klass_reg, klass); MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, tls_get->dreg, G_STRUCT_OFFSET (MonoJitTlsData, class_cast_to), to_klass_reg); + + if (null_check) { + MONO_START_BB (cfg, is_null_bb); + if (out_bblock) + *out_bblock = cfg->cbb; + } } } @@ -3386,7 +3401,7 @@ mini_emit_check_array_type (MonoCompile *cfg, MonoInst *obj, MonoClass *array_cl context_used = mini_class_check_context_used (cfg, array_class); - save_cast_details (cfg, array_class, obj->dreg); + save_cast_details (cfg, array_class, obj->dreg, FALSE, NULL); MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, obj->dreg, G_STRUCT_OFFSET (MonoObject, vtable)); @@ -3503,7 +3518,7 @@ handle_unbox (MonoCompile *cfg, MonoClass *klass, MonoInst **sp, int context_use MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, eclass_reg, element_class->dreg); MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException"); } else { - save_cast_details (cfg, klass->element_class, obj_reg); + save_cast_details (cfg, klass->element_class, obj_reg, FALSE, NULL); mini_emit_class_check (cfg, eclass_reg, klass->element_class); reset_cast_details (cfg); } @@ -3872,7 +3887,7 @@ handle_castclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src, int context MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0); MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb); - save_cast_details (cfg, klass, obj_reg); + save_cast_details (cfg, klass, obj_reg, FALSE, NULL); if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) { MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, G_STRUCT_OFFSET (MonoObject, vtable)); @@ -4182,7 +4197,7 @@ handle_ccastclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src) MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0); MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, ok_result_bb); - save_cast_details (cfg, klass, obj_reg); + save_cast_details (cfg, klass, obj_reg, FALSE, NULL); if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) { #ifndef DISABLE_REMOTING @@ -9313,7 +9328,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b EMIT_NEW_PCONST (cfg, args [2], mono_domain_alloc0 (cfg->domain, sizeof (gpointer))); /*The wrapper doesn't inline well so the bloat of inlining doesn't pay off.*/ - save_cast_details (cfg, klass, sp [0]->dreg); + + save_cast_details (cfg, klass, sp [0]->dreg, TRUE, &bblock); *sp++ = mono_emit_method_call (cfg, mono_castclass, args, NULL); reset_cast_details (cfg); ip += 5; @@ -9326,7 +9342,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b mono_castclass = mono_marshal_get_castclass (klass); iargs [0] = sp [0]; - save_cast_details (cfg, klass, sp [0]->dreg); + save_cast_details (cfg, klass, sp [0]->dreg, TRUE, &bblock); costs = inline_method (cfg, mono_castclass, mono_method_signature (mono_castclass), iargs, ip, cfg->real_offset, dont_inline, TRUE); reset_cast_details (cfg); diff --git a/mono/tests/debug-casts.cs b/mono/tests/debug-casts.cs index fafa7ef438f77..129e6e210e309 100644 --- a/mono/tests/debug-casts.cs +++ b/mono/tests/debug-casts.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; public class Tests { @@ -30,4 +31,15 @@ public class Tests } return 0; } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static object return_null () { + return null; + } + + public static int test_0_complex_1_null () { + object o = return_null (); + IEnumerable ie = (IEnumerable)o; + return 0; + } }