Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[mono] [imt] Don't increment vt_slot for non-virtual generic methods #94437

Merged
merged 4 commits into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 8 additions & 24 deletions src/mono/mono/metadata/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -1430,8 +1430,8 @@ print_imt_entry (const char* message, MonoImtBuilderEntry *e, int num) {
message,
num,
method,
method->klass->name_space,
method->klass->name,
m_class_get_name_space (method->klass),
m_class_get_name (method->klass),
method->name);
} else {
printf (" * %s: NULL\n", message);
Expand Down Expand Up @@ -1543,12 +1543,11 @@ get_generic_virtual_entries (MonoMemoryManager *mem_manager, gpointer *vtable_sl
*
*/
static void
build_imt_slots (MonoClass *klass, MonoVTable *vt, gpointer* imt, GSList *extra_interfaces, int slot_num)
build_imt_slots (MonoClass *klass, MonoVTable *vt, gpointer* imt, int slot_num)
{
MONO_REQ_GC_NEUTRAL_MODE;

int i;
GSList *list_item;
guint32 imt_collisions_bitmap = 0;
MonoImtBuilderEntry **imt_builder = (MonoImtBuilderEntry **)g_calloc (MONO_IMT_SIZE, sizeof (MonoImtBuilderEntry*));
int method_count = 0;
Expand Down Expand Up @@ -1596,8 +1595,10 @@ build_imt_slots (MonoClass *klass, MonoVTable *vt, gpointer* imt, GSList *extra_
}
method = mono_class_get_method_by_index (iface, method_slot_in_interface);
if (method->is_generic) {
has_generic_virtual = TRUE;
vt_slot ++;
if (m_method_is_virtual (method)) {
has_generic_virtual = TRUE;
vt_slot ++;
}
continue;
}

Expand All @@ -1613,23 +1614,6 @@ build_imt_slots (MonoClass *klass, MonoVTable *vt, gpointer* imt, GSList *extra_
}
}
}
if (extra_interfaces) {
int interface_offset = m_class_get_vtable_size (klass);

for (list_item = extra_interfaces; list_item != NULL; list_item=list_item->next) {
MonoClass* iface = (MonoClass *)list_item->data;
int method_slot_in_interface;
int mcount = mono_class_get_method_count (iface);
for (method_slot_in_interface = 0; method_slot_in_interface < mcount; method_slot_in_interface++) {
MonoMethod *method = mono_class_get_method_by_index (iface, method_slot_in_interface);

if (method->is_generic)
has_generic_virtual = TRUE;
add_imt_builder_entry (imt_builder, method, &imt_collisions_bitmap, interface_offset + method_slot_in_interface, slot_num);
}
interface_offset += mcount;
}
}
for (i = 0; i < MONO_IMT_SIZE; ++i) {
/* overwrite the imt slot only if we're building all the entries or if
* we're building this specific one
Expand Down Expand Up @@ -1729,7 +1713,7 @@ mono_vtable_build_imt_slot (MonoVTable* vtable, int imt_slot)
mono_loader_lock ();
/* we change the slot only if it wasn't changed from the generic imt trampoline already */
if (!callbacks.imt_entry_inited (vtable, imt_slot))
build_imt_slots (vtable->klass, vtable, imt, NULL, imt_slot);
build_imt_slots (vtable->klass, vtable, imt, imt_slot);
mono_loader_unlock ();
}

Expand Down
128 changes: 128 additions & 0 deletions src/tests/Loader/classloader/regressions/GitHub_93770/GitHub_93770.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// 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.Collections.Generic;
using System.Runtime.CompilerServices;
using Xunit;

namespace ReproGH93770;

// By default in Mono every class that implements interfaces has 19
// interface method table slots that are used to dispatch interface
// calls. The methods are assigned to slots based on a hash of some
// metadata. This test tries to create at least one IMT slot that has
// a collision by creating an interface with 20 virtual methods.
//
// The bug is that if the method also has a non-virtual generic
// method, the IMT slot with the collision calls the wrong vtable
// element.

public class ReproGH93770
{
[Fact]
public static int TestEntryPoint()
{
C c = new C();
I1 i1 = c;
Helper(i1);
return 100;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void Helper(I1 i1)
{
var e = 0;
var n = i1.M0();
Assert.Equal(e, n);
e++;
n = i1.M1();
Assert.Equal(e, n);
e++;
n = i1.M2();
Assert.Equal(e, n);
e++;
n = i1.M3();
Assert.Equal(e, n);
e++;
n = i1.M4();
Assert.Equal(e, n);
e++;
n = i1.M5();
Assert.Equal(e, n);
e++;
n = i1.M6();
Assert.Equal(e, n);
e++;
n = i1.M7();
Assert.Equal(e, n);
e++;
n = i1.M8();
Assert.Equal(e, n);
e++;
n = i1.M9();
Assert.Equal(e, n);
e++;
n = i1.M10();
Assert.Equal(e, n);
e++;
n = i1.M11();
Assert.Equal(e, n);
e++;
n = i1.M12();
Assert.Equal(e, n);
e++;
n = i1.M13();
Assert.Equal(e, n);
e++;
n = i1.M14();
Assert.Equal(e, n);
e++;
n = i1.M15();
Assert.Equal(e, n);
e++;
n = i1.M16();
Assert.Equal(e, n);
e++;
n = i1.M17();
Assert.Equal(e, n);
e++;
n = i1.M18();
Assert.Equal(e, n);
e++;
n = i1.M19();
Assert.Equal(e, n);
e++;
}
}

public interface I1 {
public static T Id<T> (T t)=> t;

public int M0() => 0;
public int M1() => 1;
public int M2() => 2;
public int M3() => 3;
public int M4() => 4;

public int M5() => 5;
public int M6() => 6;
public int M7() => 7;
public int M8() => 8;
public int M9() => 9;
public int M10() => 10;
public int M11() => 11;
public int M12() => 12;
public int M13() => 13;
public int M14() => 14;
public int M15() => 15;
public int M16() => 16;
public int M17() => 17;
public int M18() => 18;
public int M19() => 19;

}

public class C : I1 {

}


Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
</PropertyGroup>
<ItemGroup>
<Compile Include="GitHub_93770.cs" />
</ItemGroup>
</Project>
Loading