Skip to content

Commit ca8d6f0

Browse files
authored
[mono][aot] Type load checks do not fail at compile time but produce a runtime exception (#91261)
* Enable tests. * When AOTing, type checks do not fail compilation but create a runtime exception. * Cleaned up type load error cleaning. TypeLoadException icall now has a message with type name. * Removed another instance of indiscriminate exception clearing. * Fixed build warning. * Using class const instead of string const. Reverted some compile to runtime errors that were not necessary for the unit tests. * White space. * Fixed build warning. * Trying to fix weird AOT errors, fixed type load throw function. * Fixed build error. * Special handling for classes that are NULL. * Providing for a null klass when generating exception. * Removed flow control directive from macro. * Fixed stack corruption. * Attempt to push the correct type onto the stack. * Fixing uninitialized ins. * Fixing ro_type. * Initializing ins. * Complex cases with type load failures replace method body with a throw. * Cleaning up superfluous code changes. * Restored sizeof cosntant on failed types.
1 parent d1d6a6b commit ca8d6f0

File tree

7 files changed

+138
-19
lines changed

7 files changed

+138
-19
lines changed

src/mono/mono/metadata/jit-icall-reg.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ MONO_JIT_ICALL (mono_throw_bad_image) \
312312
MONO_JIT_ICALL (mono_throw_not_supported) \
313313
MONO_JIT_ICALL (mono_throw_platform_not_supported) \
314314
MONO_JIT_ICALL (mono_throw_invalid_program) \
315+
MONO_JIT_ICALL (mono_throw_type_load) \
315316
MONO_JIT_ICALL (mono_trace_enter_method) \
316317
MONO_JIT_ICALL (mono_trace_leave_method) \
317318
MONO_JIT_ICALL (mono_trace_tail_method) \

src/mono/mono/mini/jit-icalls.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,6 +1688,22 @@ mono_throw_invalid_program (const char *msg)
16881688
mono_error_set_pending_exception (error);
16891689
}
16901690

1691+
void
1692+
mono_throw_type_load (MonoClass* klass)
1693+
{
1694+
ERROR_DECL (error);
1695+
1696+
if (G_UNLIKELY(!klass)) {
1697+
mono_error_set_generic_error (error, "System", "TypeLoadException", "");
1698+
} else {
1699+
char* klass_name = mono_type_get_full_name (klass);
1700+
mono_error_set_type_load_class (error, klass, "Attempting to load invalid type '%s'.", klass_name);
1701+
g_free (klass_name);
1702+
}
1703+
1704+
mono_error_set_pending_exception (error);
1705+
}
1706+
16911707
void
16921708
mono_dummy_jit_icall (void)
16931709
{

src/mono/mono/mini/jit-icalls.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,8 @@ ICALL_EXPORT void mono_throw_platform_not_supported (void);
234234

235235
ICALL_EXPORT void mono_throw_invalid_program (const char *msg);
236236

237+
ICALL_EXPORT void mono_throw_type_load (MonoClass* klass);
238+
237239
ICALL_EXPORT void mono_dummy_jit_icall (void);
238240

239241
ICALL_EXPORT void mono_dummy_jit_icall_val (gpointer ptr);

src/mono/mono/mini/method-to-ir.c

Lines changed: 111 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ mono_tailcall_print (const char *format, ...)
167167

168168
#define TYPE_LOAD_ERROR(klass) do { \
169169
cfg->exception_ptr = klass; \
170-
LOAD_ERROR; \
170+
LOAD_ERROR; \
171171
} while (0)
172172

173173
#define CHECK_CFG_ERROR do {\
@@ -2303,6 +2303,18 @@ emit_not_supported_failure (MonoCompile *cfg)
23032303
mono_emit_jit_icall (cfg, mono_throw_not_supported, NULL);
23042304
}
23052305

2306+
static void
2307+
emit_type_load_failure (MonoCompile* cfg, MonoClass* klass)
2308+
{
2309+
MonoInst* iargs[1];
2310+
if (G_LIKELY (klass)) {
2311+
EMIT_NEW_CLASSCONST (cfg, iargs [0], klass);
2312+
} else {
2313+
EMIT_NEW_PCONST (cfg, iargs [0], NULL);
2314+
}
2315+
mono_emit_jit_icall (cfg, mono_throw_type_load, iargs);
2316+
}
2317+
23062318
static void
23072319
emit_invalid_program_with_msg (MonoCompile *cfg, MonoError *error_msg, MonoMethod *caller, MonoMethod *callee)
23082320
{
@@ -4982,6 +4994,15 @@ inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig,
49824994
#define CHECK_UNVERIFIABLE(cfg) if (cfg->unverifiable) UNVERIFIED
49834995
#define CHECK_TYPELOAD(klass) if (!(klass) || mono_class_has_failure (klass)) TYPE_LOAD_ERROR ((klass))
49844996

4997+
#define CLEAR_TYPELOAD_EXCEPTION(cfg) if (cfg->exception_type == MONO_EXCEPTION_TYPE_LOAD) { clear_cfg_error (cfg); cfg->exception_type = MONO_EXCEPTION_NONE; }
4998+
#define CLASS_HAS_FAILURE(klass) (!(klass) || mono_class_has_failure (klass))
4999+
#define HANDLE_TYPELOAD_ERROR(cfg,klass) do { \
5000+
if (!cfg->compile_aot) \
5001+
TYPE_LOAD_ERROR ((klass)); \
5002+
emit_type_load_failure (cfg, klass); \
5003+
CLEAR_TYPELOAD_EXCEPTION (cfg); \
5004+
} while (0)
5005+
49855006
/* offset from br.s -> br like opcodes */
49865007
#define BIG_BRANCH_OFFSET 13
49875008

@@ -5457,7 +5478,9 @@ emit_optimized_ldloca_ir (MonoCompile *cfg, guchar *ip, guchar *end, int local)
54575478
if ((ip = il_read_initobj (ip, end, &token)) && ip_in_bb (cfg, cfg->cbb, start + 1)) {
54585479
/* From the INITOBJ case */
54595480
klass = mini_get_class (cfg->current_method, token, cfg->generic_context);
5460-
CHECK_TYPELOAD (klass);
5481+
if (CLASS_HAS_FAILURE (klass)) {
5482+
HANDLE_TYPELOAD_ERROR (cfg, klass);
5483+
}
54615484
type = mini_get_underlying_type (m_class_get_byval_arg (klass));
54625485
emit_init_local (cfg, local, type, TRUE);
54635486
return ip;
@@ -6189,6 +6212,49 @@ emit_llvmonly_interp_entry (MonoCompile *cfg, MonoMethodHeader *header)
61896212
link_bblock (cfg, cfg->cbb, cfg->bb_exit);
61906213
}
61916214

6215+
static void
6216+
method_make_alwaysthrow_typeloadfailure (MonoCompile* cfg, MonoClass* klass)
6217+
{
6218+
// Get rid of all out-BBs from the entry BB. (all but init BB)
6219+
for (gint16 i = cfg->bb_entry->out_count - 1; i >= 0; i--) {
6220+
if (cfg->bb_entry->out_bb [i] != cfg->bb_init) {
6221+
mono_unlink_bblock (cfg, cfg->bb_entry, cfg->bb_entry->out_bb [i]);
6222+
mono_remove_bblock (cfg, cfg->bb_entry->out_bb [i]);
6223+
}
6224+
}
6225+
6226+
// Discard all out-BBs from the init BB.
6227+
for (gint16 i = cfg->bb_init->out_count - 1; i >= 0; i--) {
6228+
if (cfg->bb_init->out_bb [i] != cfg->bb_exit) {
6229+
mono_unlink_bblock (cfg, cfg->bb_init, cfg->bb_init->out_bb [i]);
6230+
mono_remove_bblock (cfg, cfg->bb_init->out_bb [i]);
6231+
}
6232+
}
6233+
6234+
// Maintain linked list consistency. This BB should have been added as the last,
6235+
// ignoring the ones that held actual method code.
6236+
cfg->cbb = cfg->bb_init;
6237+
6238+
// Create a new BB that only throws, link it after the entry.
6239+
MonoBasicBlock* bb;
6240+
NEW_BBLOCK (cfg, bb);
6241+
bb->cil_code = NULL;
6242+
bb->cil_length = 0;
6243+
cfg->cbb->next_bb = bb;
6244+
cfg->cbb = bb;
6245+
6246+
emit_type_load_failure (cfg, klass);
6247+
MonoInst* ins;
6248+
MONO_INST_NEW (cfg, ins, OP_NOT_REACHED);
6249+
MONO_ADD_INS (cfg->cbb, ins);
6250+
6251+
ADD_BBLOCK (cfg, bb);
6252+
mono_link_bblock (cfg, cfg->bb_init, bb);
6253+
mono_link_bblock (cfg, bb, cfg->bb_exit);
6254+
6255+
cfg->disable_inline = TRUE;
6256+
}
6257+
61926258
typedef union _MonoOpcodeParameter {
61936259
gint32 i32;
61946260
gint64 i64;
@@ -9900,6 +9966,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
99009966
MonoType *ftype;
99019967
MonoInst *store_val = NULL;
99029968
MonoInst *thread_ins;
9969+
ins = NULL;
99039970

99049971
is_instance = (il_op == MONO_CEE_LDFLD || il_op == MONO_CEE_LDFLDA || il_op == MONO_CEE_STFLD);
99059972
if (is_instance) {
@@ -9927,8 +9994,28 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
99279994
else {
99289995
klass = NULL;
99299996
field = mono_field_from_token_checked (image, token, &klass, generic_context, cfg->error);
9930-
if (!field)
9931-
CHECK_TYPELOAD (klass);
9997+
if (!field || CLASS_HAS_FAILURE (klass)) {
9998+
HANDLE_TYPELOAD_ERROR (cfg, klass);
9999+
10000+
// Reached only in AOT. Cannot turn a token into a class. We silence the compilation error
10001+
// and generate a runtime exception.
10002+
if (cfg->error->error_code == MONO_ERROR_BAD_IMAGE)
10003+
clear_cfg_error (cfg);
10004+
10005+
// We need to push a dummy value onto the stack, respecting the intended type.
10006+
if (il_op == MONO_CEE_LDFLDA || il_op == MONO_CEE_LDSFLDA) {
10007+
// Address is expected, push a null pointer.
10008+
EMIT_NEW_PCONST (cfg, *sp, NULL);
10009+
sp++;
10010+
} else if (il_op == MONO_CEE_LDFLD || il_op == MONO_CEE_LDSFLD) {
10011+
// An object is expected here. It may be impossible to correctly infer its type,
10012+
// we turn this entire method into a throw.
10013+
method_make_alwaysthrow_typeloadfailure (cfg, klass);
10014+
goto all_bbs_done;
10015+
}
10016+
10017+
break;
10018+
}
993210019
CHECK_CFG_ERROR;
993310020
}
993410021
if (!dont_verify && !cfg->skip_visibility && !mono_method_can_access_field (method, field))
@@ -10394,8 +10481,11 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
1039410481
}
1039510482

1039610483
if (!is_const) {
10397-
MonoInst *load;
10484+
// This can happen in case of type load error.
10485+
if (!ins)
10486+
EMIT_NEW_PCONST (cfg, ins, 0);
1039810487

10488+
MonoInst *load;
1039910489
EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, field->type, ins->dreg, 0);
1040010490
load->flags |= ins_flag;
1040110491
*sp++ = load;
@@ -11882,13 +11972,20 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
1188211972
inline_costs += 100000;
1188311973
break;
1188411974
case MONO_CEE_INITOBJ:
11885-
--sp;
1188611975
klass = mini_get_class (method, token, generic_context);
11887-
CHECK_TYPELOAD (klass);
11976+
if (CLASS_HAS_FAILURE (klass)) {
11977+
HANDLE_TYPELOAD_ERROR (cfg, klass);
11978+
inline_costs += 10;
11979+
break; // reached only in AOT
11980+
}
11981+
11982+
--sp;
11983+
1188811984
if (mini_class_is_reference (klass))
1188911985
MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, sp [0]->dreg, 0, 0);
1189011986
else
1189111987
mini_emit_initobj (cfg, *sp, NULL, klass);
11988+
1189211989
inline_costs += 1;
1189311990
break;
1189411991
case MONO_CEE_CONSTRAINED_:
@@ -11978,7 +12075,12 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
1197812075
EMIT_NEW_ICONST (cfg, ins, val);
1197912076
} else {
1198012077
klass = mini_get_class (method, token, generic_context);
11981-
CHECK_TYPELOAD (klass);
12078+
if (CLASS_HAS_FAILURE (klass)) {
12079+
HANDLE_TYPELOAD_ERROR (cfg, klass);
12080+
EMIT_NEW_ICONST(cfg, ins, 0);
12081+
*sp++ = ins;
12082+
break;
12083+
}
1198212084

1198312085
if (mini_is_gsharedvt_klass (klass)) {
1198412086
ins = mini_emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_CLASS_SIZEOF);
@@ -12031,6 +12133,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
1203112133
}
1203212134
if (start_new_bblock != 1)
1203312135
UNVERIFIED;
12136+
all_bbs_done:
1203412137

1203512138
cfg->cbb->cil_length = GPTRDIFF_TO_INT32 (ip - cfg->cbb->cil_code);
1203612139
if (cfg->cbb->next_bb) {

src/mono/mono/mini/mini-runtime.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5144,6 +5144,7 @@ register_icalls (void)
51445144
register_icall (mono_throw_not_supported, mono_icall_sig_void, FALSE);
51455145
register_icall (mono_throw_platform_not_supported, mono_icall_sig_void, FALSE);
51465146
register_icall (mono_throw_invalid_program, mono_icall_sig_void_ptr, FALSE);
5147+
register_icall (mono_throw_type_load, mono_icall_sig_void_ptr, FALSE);
51475148
register_icall_no_wrapper (mono_dummy_jit_icall, mono_icall_sig_void);
51485149
//register_icall_no_wrapper (mono_dummy_jit_icall_val, mono_icall_sig_void_ptr);
51495150
register_icall_no_wrapper (mini_init_method_rgctx, mono_icall_sig_void_ptr_ptr);

src/mono/mono/mini/mini.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,9 @@ mono_compile_create_var_for_vreg (MonoCompile *cfg, MonoType *type, int opcode,
660660
inst->backend.is_pinvoke = 0;
661661
inst->dreg = vreg;
662662

663-
if (mono_class_has_failure (inst->klass))
663+
// In AOT, we do not set the exception so that the compilation can succeed. To indicate
664+
// the error, an exception is thrown in run-time.
665+
if (!cfg->compile_aot && mono_class_has_failure (inst->klass))
664666
mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
665667

666668
if (cfg->compute_gc_maps) {
@@ -1351,7 +1353,10 @@ mono_allocate_stack_slots (MonoCompile *cfg, gboolean backward, guint32 *stack_s
13511353
size = mini_type_stack_size (t, &ialign);
13521354
align = ialign;
13531355

1354-
if (mono_class_has_failure (mono_class_from_mono_type_internal (t)))
1356+
// In AOT, we do not set the exception but allow the compilation to succeed. The error will be
1357+
// indicated in runtime by throwing an exception when an operation with the invalid object is
1358+
// attempted.
1359+
if (!cfg->compile_aot && mono_class_has_failure (mono_class_from_mono_type_internal (t)))
13551360
mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
13561361

13571362
if (mini_class_is_simd (cfg, mono_class_from_mono_type_internal (t)))

src/tests/issues.targets

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2387,15 +2387,6 @@
23872387
<ExcludeList Include="$(XunitTestBinBase)/Loader/classloader/explicitlayout/objrefandnonobjrefoverlap/case9/**">
23882388
<Issue>expected failure: overlapped structs fail at AOT compile time, not runtime</Issue>
23892389
</ExcludeList>
2390-
<ExcludeList Include = "$(XunitTestBinBase)/Loader/classloader/explicitlayout/NestedStructs/case03/**">
2391-
<Issue>expected failure: overlapped structs fail at AOT compile time, not runtime</Issue>
2392-
</ExcludeList>
2393-
<ExcludeList Include = "$(XunitTestBinBase)/Loader/classloader/explicitlayout/NestedStructs/case04/**">
2394-
<Issue>expected failure: overlapped structs fail at AOT compile time, not runtime</Issue>
2395-
</ExcludeList>
2396-
<ExcludeList Include = "$(XunitTestBinBase)/Loader/classloader/explicitlayout/NestedStructs/case05/**">
2397-
<Issue>expected failure: overlapped structs fail at AOT compile time, not runtime</Issue>
2398-
</ExcludeList>
23992390
<ExcludeList Include = "$(XunitTestBinBase)/Loader/classloader/RefFields/Validate/**">
24002391
<Issue>expected failure: unsupported type with ref field fails at AOT compile time, not runtime</Issue>
24012392
</ExcludeList>

0 commit comments

Comments
 (0)