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] Fix uninitialized vtable bug #67746

Merged
merged 13 commits into from
Apr 12, 2022
Merged
Show file tree
Hide file tree
Changes from 6 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
39 changes: 21 additions & 18 deletions src/mono/mono/metadata/icall.c
Original file line number Diff line number Diff line change
Expand Up @@ -3248,44 +3248,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, MonoError *error, int begin_slot, int end_slot)
simonrozsival marked this conversation as resolved.
Show resolved Hide resolved
{
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, error, io_stream_begin_read_slot, io_stream_end_read_slot);
}

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, error, io_stream_begin_write_slot, io_stream_end_write_slot);
}

MonoBoolean
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<RunAOTCompilation>true</RunAOTCompilation>
<MonoForceInterpreter>false</MonoForceInterpreter>
<TestRuntime>true</TestRuntime>
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
<MainLibraryFileName>Android.Device_Emulator.Aot_System.IO.Stream.Test.dll</MainLibraryFileName>
<ExpectedExitCode>42</ExpectedExitCode>
<EnableAggressiveTrimming>true</EnableAggressiveTrimming>
</PropertyGroup>

<ItemGroup>
<Compile Include="Program.cs" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -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;
}
}