Skip to content

Commit

Permalink
[dim] Emit error when calling abstract methods (#15433)
Browse files Browse the repository at this point in the history
* Emitting bad image exception when a non virtual call is calling an abstract method.

* Fixing side-effect.
Implementing the same thing on interpreter.
Adding unit test.
  • Loading branch information
thaystg committed Jun 28, 2019
1 parent 064d367 commit c011456
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 7 deletions.
1 change: 1 addition & 0 deletions mono/metadata/jit-icall-reg.h
Expand Up @@ -302,6 +302,7 @@ MONO_JIT_ICALL (mono_threads_exit_gc_unsafe_region_unbalanced) \
MONO_JIT_ICALL (mono_threads_state_poll) \
MONO_JIT_ICALL (mono_throw_exception) \
MONO_JIT_ICALL (mono_throw_method_access) \
MONO_JIT_ICALL (mono_throw_bad_image) \
MONO_JIT_ICALL (mono_trace_enter_method) \
MONO_JIT_ICALL (mono_trace_leave_method) \
MONO_JIT_ICALL (mono_upgrade_remote_class_wrapper) \
Expand Down
18 changes: 15 additions & 3 deletions mono/mini/interp/transform.c
Expand Up @@ -882,6 +882,15 @@ interp_generate_mae_throw (TransformData *td, MonoMethod *method, MonoMethod *ta
td->sp -= 2;
}

static void
interp_generate_bie_throw (TransformData *td)
{
MonoJitICallInfo *info = &mono_get_jit_icall_info ()->mono_throw_bad_image;

interp_add_ins (td, MINT_ICALL_PP_V);
td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func);
}

/*
* These are additional locals that can be allocated as we transform the code.
* They are allocated past the method locals so they are accessed in the same
Expand Down Expand Up @@ -1950,9 +1959,12 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target
if (target_method)
mono_class_init_internal (target_method->klass);

if (!is_virtual && target_method && (target_method->flags & METHOD_ATTRIBUTE_ABSTRACT))
/* MS.NET seems to silently convert this to a callvirt */
is_virtual = TRUE;
if (!is_virtual && target_method && (target_method->flags & METHOD_ATTRIBUTE_ABSTRACT)) {
if (!mono_class_is_interface (method->klass))
interp_generate_bie_throw (td);
else
is_virtual = TRUE;
}

if (is_virtual && target_method && (!(target_method->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
(MONO_METHOD_IS_FINAL (target_method) &&
Expand Down
8 changes: 8 additions & 0 deletions mono/mini/jit-icalls.c
Expand Up @@ -1522,6 +1522,14 @@ mono_throw_method_access (MonoMethod *caller, MonoMethod *callee)
g_free (caller_name);
}

void
mono_throw_bad_image ()
{
ERROR_DECL (error);
mono_error_set_generic_error (error, "System", "BadImageFormatException", "Bad IL format.");
mono_error_set_pending_exception (error);
}

void
mono_dummy_jit_icall (void)
{
Expand Down
2 changes: 2 additions & 0 deletions mono/mini/jit-icalls.h
Expand Up @@ -219,6 +219,8 @@ G_EXTERN_C double mono_ckfinite (double d);

G_EXTERN_C void mono_throw_method_access (MonoMethod *caller, MonoMethod *callee);

G_EXTERN_C void mono_throw_bad_image (void);

G_EXTERN_C void mono_dummy_jit_icall (void);

#endif /* __MONO_JIT_ICALLS_H__ */
16 changes: 12 additions & 4 deletions mono/mini/method-to-ir.c
Expand Up @@ -2244,6 +2244,12 @@ emit_method_access_failure (MonoCompile *cfg, MonoMethod *caller, MonoMethod *ca
mono_emit_jit_icall (cfg, mono_throw_method_access, args);
}

static void
emit_bad_image_failure (MonoCompile *cfg, MonoMethod *caller, MonoMethod *callee)
{
mono_emit_jit_icall (cfg, mono_throw_bad_image, NULL);
}

static MonoMethod*
get_method_nofail (MonoClass *klass, const char *method_name, int num_params, int flags)
{
Expand Down Expand Up @@ -7038,9 +7044,12 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
if (mono_security_core_clr_enabled ())
ensure_method_is_allowed_to_call_method (cfg, method, cil_method);

if (!virtual_ && (cmethod->flags & METHOD_ATTRIBUTE_ABSTRACT))
/* MS.NET seems to silently convert this to a callvirt */
virtual_ = TRUE;
if (!virtual_ && (cmethod->flags & METHOD_ATTRIBUTE_ABSTRACT)) {
if (!mono_class_is_interface (method->klass))
emit_bad_image_failure (cfg, method, cil_method);
else
virtual_ = TRUE;
}

{
/*
Expand Down Expand Up @@ -7811,7 +7820,6 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
}
emit_seq_point (cfg, method, next_ip, FALSE, TRUE);
}

break;
}
case MONO_CEE_RET:
Expand Down
1 change: 1 addition & 0 deletions mono/mini/mini-runtime.c
Expand Up @@ -4628,6 +4628,7 @@ register_icalls (void)
register_icall (mono_get_assembly_object, mono_icall_sig_object_ptr, TRUE);
register_icall (mono_get_method_object, mono_icall_sig_object_ptr, TRUE);
register_icall (mono_throw_method_access, mono_icall_sig_void_ptr_ptr, FALSE);
register_icall (mono_throw_bad_image, mono_icall_sig_void, FALSE);
register_icall_no_wrapper (mono_dummy_jit_icall, mono_icall_sig_void);

register_icall_with_wrapper (mono_monitor_enter_internal, mono_icall_sig_int32_obj);
Expand Down
1 change: 1 addition & 0 deletions mono/tests/Makefile.am
Expand Up @@ -1011,6 +1011,7 @@ TESTS_IL_SRC= \
dim-protected-accessibility2.il \
dim-reabstraction.il \
dim-reabstraction-generics.il \
dim-abstractcall.il \
twopassvariance.il \
tailcall-generic-cast-conservestack-il.il \
tailcall-generic-cast-nocrash-il.il \
Expand Down
115 changes: 115 additions & 0 deletions mono/tests/dim-abstractcall.il
@@ -0,0 +1,115 @@

// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0
// Copyright (c) Microsoft Corporation. All rights reserved.



// Metadata version: v4.0.30319.assembly extern mscorlib { }
.assembly abstractcalls { }

.class interface public abstract auto ansi I1
{
.method public hidebysig newslot virtual abstract
instance int32 Add(int32 x) cil managed
{
}
}

.class public abstract auto ansi C1
extends [mscorlib]System.Object
implements I1
{
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}

.method public hidebysig newslot virtual abstract
instance int32 Remove(int32 x) cil managed
{
}
}

.class public auto ansi C2
extends [mscorlib]System.Object
implements I1
{
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void C1::.ctor()
ret
}

.method public hidebysig virtual
instance int32 Remove(int32 x) cil managed
{
ldc.i4.0
ret
}

.method public hidebysig newslot virtual
instance int32 Add(int32 x) cil managed
{
ldc.i4.5
ret
}
}

.method private hidebysig static void CallClass() cil managed
{
newobj instance void C2::.ctor()
ldc.i4.0
call instance int32 C1::Remove(int32)
pop
ret
}

.method private hidebysig static void CallInterface() cil managed
{
newobj instance void C2::.ctor()
ldc.i4.0
call instance int32 I1::Add(int32)
pop
ret
}

.method private hidebysig static int32 Main() cil managed
{
.entrypoint

.try
{
call void CallClass()
leave Fail
}
catch [System.Runtime]System.BadImageFormatException
{
leave AbstractClassOK
}

AbstractClassOK:

.try
{
call void CallInterface()
leave Fail
}
catch [System.Runtime]System.BadImageFormatException
{
leave AbstractInterfaceOK
}

AbstractInterfaceOK:
ldc.i4 0
ret

Fail:
ldc.i4 155
ret
}

0 comments on commit c011456

Please sign in to comment.