From c3fcafb5235401609ece803553fa3432d913c444 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 13 Apr 2022 08:55:05 -0700 Subject: [PATCH] [release/6.0] [Mono] Fix uninitialized vtable bug (#67759) * Add functional test * Fix vtable setup * Add suggested code changes * Improve arguments ordering Co-authored-by: Simon Rozsival --- src/mono/mono/metadata/icall.c | 39 ++++++++------- ..._Emulator.Aot_System.IO.Stream.Test.csproj | 16 +++++++ .../AOT_System.IO.Stream/Program.cs | 48 +++++++++++++++++++ 3 files changed, 85 insertions(+), 18 deletions(-) create mode 100644 src/tests/FunctionalTests/Android/Device_Emulator/AOT_System.IO.Stream/Android.Device_Emulator.Aot_System.IO.Stream.Test.csproj create mode 100644 src/tests/FunctionalTests/Android/Device_Emulator/AOT_System.IO.Stream/Program.cs diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index b1e2d737f2246..a671c64117507 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -3227,44 +3227,47 @@ init_io_stream_slots (void) io_stream_slots_set = TRUE; } -MonoBoolean -ves_icall_System_IO_Stream_HasOverriddenBeginEndRead (MonoObjectHandle stream, MonoError *error) + +static MonoBoolean +stream_has_overriden_begin_or_end_method (MonoObjectHandle stream, int begin_slot, int end_slot, MonoError *error) { MonoClass* curr_klass = MONO_HANDLE_GET_CLASS (stream); MonoClass* base_klass = mono_class_try_get_stream_class (); - if (!io_stream_slots_set) - init_io_stream_slots (); + mono_class_setup_vtable (curr_klass); + if (mono_class_has_failure (curr_klass)) { + mono_error_set_for_class_failure (error, curr_klass); + return_val_if_nok (error, FALSE); + } // slots can still be -1 and it means Linker removed the methods from the base class (Stream) // in this case we can safely assume the methods are not overridden // otherwise - check vtable MonoMethod **curr_klass_vtable = m_class_get_vtable (curr_klass); - gboolean begin_read_is_overriden = io_stream_begin_read_slot != -1 && curr_klass_vtable [io_stream_begin_read_slot]->klass != base_klass; - gboolean end_read_is_overriden = io_stream_end_read_slot != -1 && curr_klass_vtable [io_stream_end_read_slot]->klass != base_klass; + gboolean begin_is_overriden = begin_slot != -1 && curr_klass_vtable [begin_slot] != NULL && curr_klass_vtable [begin_slot]->klass != base_klass; + gboolean end_is_overriden = end_slot != -1 && curr_klass_vtable [end_slot] != NULL && curr_klass_vtable [end_slot]->klass != base_klass; + + return begin_is_overriden || end_is_overriden; +} + +MonoBoolean +ves_icall_System_IO_Stream_HasOverriddenBeginEndRead (MonoObjectHandle stream, MonoError *error) +{ + if (!io_stream_slots_set) + init_io_stream_slots (); // return true if BeginRead or EndRead were overriden - return begin_read_is_overriden || end_read_is_overriden; + return stream_has_overriden_begin_or_end_method (stream, io_stream_begin_read_slot, io_stream_end_read_slot, error); } MonoBoolean ves_icall_System_IO_Stream_HasOverriddenBeginEndWrite (MonoObjectHandle stream, MonoError *error) { - MonoClass* curr_klass = MONO_HANDLE_GETVAL (stream, vtable)->klass; - MonoClass* base_klass = mono_class_try_get_stream_class (); - if (!io_stream_slots_set) init_io_stream_slots (); - // slots can still be -1 and it means Linker removed the methods from the base class (Stream) - // in this case we can safely assume the methods are not overridden - // otherwise - check vtable - MonoMethod **curr_klass_vtable = m_class_get_vtable (curr_klass); - gboolean begin_write_is_overriden = io_stream_begin_write_slot != -1 && curr_klass_vtable [io_stream_begin_write_slot]->klass != base_klass; - gboolean end_write_is_overriden = io_stream_end_write_slot != -1 && curr_klass_vtable [io_stream_end_write_slot]->klass != base_klass; - // return true if BeginWrite or EndWrite were overriden - return begin_write_is_overriden || end_write_is_overriden; + return stream_has_overriden_begin_or_end_method (stream, io_stream_begin_write_slot, io_stream_end_write_slot, error); } MonoBoolean diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/AOT_System.IO.Stream/Android.Device_Emulator.Aot_System.IO.Stream.Test.csproj b/src/tests/FunctionalTests/Android/Device_Emulator/AOT_System.IO.Stream/Android.Device_Emulator.Aot_System.IO.Stream.Test.csproj new file mode 100644 index 0000000000000..c8d2f37c34880 --- /dev/null +++ b/src/tests/FunctionalTests/Android/Device_Emulator/AOT_System.IO.Stream/Android.Device_Emulator.Aot_System.IO.Stream.Test.csproj @@ -0,0 +1,16 @@ + + + Exe + true + false + true + $(NetCoreAppCurrent) + Android.Device_Emulator.Aot_System.IO.Stream.Test.dll + 42 + true + + + + + + diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/AOT_System.IO.Stream/Program.cs b/src/tests/FunctionalTests/Android/Device_Emulator/AOT_System.IO.Stream/Program.cs new file mode 100644 index 0000000000000..29215aeb77714 --- /dev/null +++ b/src/tests/FunctionalTests/Android/Device_Emulator/AOT_System.IO.Stream/Program.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#nullable enable + +using System; +using System.IO; + +// https://github.com/dotnet/runtime/issues/67402 + +public static class Program +{ + public static int Main(string[] args) + { + var stream = new DummyStream(); + var buffer = new byte[stream.Length]; + int read = stream.ReadAsync(buffer, 0, buffer.Length).GetAwaiter().GetResult(); + return read + buffer[0]; + } + + private sealed class DummyStream : System.IO.Stream + { + protected override void Dispose (bool disposing) => throw new NotImplementedException (); + + public override int Read (byte[] buffer, int offset, int count) + { + buffer[0] = 41; + return 1; + } + + public override long Seek (long offset, SeekOrigin origin) => 0; + public override void SetLength (long value) {} + public override void Write (byte[] buffer, int offset, int count) {} + public override void Flush () {} + + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) + { + Console.WriteLine("BeginRead"); + return base.BeginRead(buffer, offset, count, callback, state); + } + + public override bool CanRead => true; + public override bool CanSeek => false; + public override bool CanWrite => false; + + public override long Length => 1; + public override long Position { get; set; } = 0; + } +}