hle: Make Ryujinx.HLE project entirely safe (#2789)

* Remove a bit of unsafety around

* Regenerate StructArrayHelpers with a max element value of 256

* hle: remove unsafe marker from all struct that had it

* hle: make SoftwareKeyboardRenderer.TryCopyTo safe

* hle: remove unsafety in NpadDevice and remove AllowUnsafeBlocks from csproj

* Revert "Regenerate StructArrayHelpers with a max element value of 256"

This reverts commit f32a6e5be094f50571970eb1116b65c080781d05.

* Introduce ByteArray of various size and use that instead of ArrayXXX to avoid stackoverflow in .NET runtime type resolution

* Use ByteArray more

* Add some missing spaces on Pack = 1 for various structs

* Fix broken logic for TryCopyTo

* Address gdkchan's comment

* Address gdkchan's comment
This commit is contained in:
Mary 2021-11-01 23:38:13 +01:00 committed by GitHub
parent e48530e9d9
commit f41687f4c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 175 additions and 126 deletions

View file

@ -0,0 +1,77 @@
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.Common.Memory
{
[StructLayout(LayoutKind.Sequential, Size = Size, Pack = 1)]
public struct ByteArray128 : IArray<byte>
{
private const int Size = 128;
byte _element;
public int Length => Size;
public ref byte this[int index] => ref ToSpan()[index];
public Span<byte> ToSpan() => MemoryMarshal.CreateSpan(ref _element, Size);
}
[StructLayout(LayoutKind.Sequential, Size = Size, Pack = 1)]
public struct ByteArray256 : IArray<byte>
{
private const int Size = 256;
byte _element;
public int Length => Size;
public ref byte this[int index] => ref ToSpan()[index];
public Span<byte> ToSpan() => MemoryMarshal.CreateSpan(ref _element, Size);
}
[StructLayout(LayoutKind.Sequential, Size = Size, Pack = 1)]
public struct ByteArray512 : IArray<byte>
{
private const int Size = 512;
byte _element;
public int Length => Size;
public ref byte this[int index] => ref ToSpan()[index];
public Span<byte> ToSpan() => MemoryMarshal.CreateSpan(ref _element, Size);
}
[StructLayout(LayoutKind.Sequential, Size = Size, Pack = 1)]
public struct ByteArray1024 : IArray<byte>
{
private const int Size = 1024;
byte _element;
public int Length => Size;
public ref byte this[int index] => ref ToSpan()[index];
public Span<byte> ToSpan() => MemoryMarshal.CreateSpan(ref _element, Size);
}
[StructLayout(LayoutKind.Sequential, Size = Size, Pack = 1)]
public struct ByteArray2048 : IArray<byte>
{
private const int Size = 2048;
byte _element;
public int Length => Size;
public ref byte this[int index] => ref ToSpan()[index];
public Span<byte> ToSpan() => MemoryMarshal.CreateSpan(ref _element, Size);
}
[StructLayout(LayoutKind.Sequential, Size = Size, Pack = 1)]
public struct ByteArray4096 : IArray<byte>
{
private const int Size = 4096;
byte _element;
public int Length => Size;
public ref byte this[int index] => ref ToSpan()[index];
public Span<byte> ToSpan() => MemoryMarshal.CreateSpan(ref _element, Size);
}
}

View file

@ -1,10 +1,12 @@
namespace Ryujinx.HLE.HOS.Applets.Browser using Ryujinx.Common.Memory;
namespace Ryujinx.HLE.HOS.Applets.Browser
{ {
public unsafe struct WebCommonReturnValue public struct WebCommonReturnValue
{ {
public WebExitReason ExitReason; public WebExitReason ExitReason;
public uint Padding; public uint Padding;
public fixed byte LastUrl[0x1000]; public ByteArray4096 LastUrl;
public ulong LastUrlSize; public ulong LastUrlSize;
} }
} }

View file

@ -24,8 +24,7 @@ namespace Ryujinx.HLE.HOS.Applets
_system = system; _system = system;
} }
unsafe public ResultCode Start(AppletSession normalSession, public ResultCode Start(AppletSession normalSession, AppletSession interactiveSession)
AppletSession interactiveSession)
{ {
_normalSession = normalSession; _normalSession = normalSession;

View file

@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Applets namespace Ryujinx.HLE.HOS.Applets
{ {
#pragma warning disable CS0649 #pragma warning disable CS0649
[StructLayout(LayoutKind.Sequential, Pack=1)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ControllerSupportArgHeader struct ControllerSupportArgHeader
{ {
public sbyte PlayerCountMin; public sbyte PlayerCountMin;

View file

@ -1,16 +1,26 @@
using Ryujinx.Common.Memory;
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Applets namespace Ryujinx.HLE.HOS.Applets
{ {
#pragma warning disable CS0649 #pragma warning disable CS0649
// (8.0.0+ version) // (8.0.0+ version)
[StructLayout(LayoutKind.Sequential, Pack=1)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct ControllerSupportArgV7 struct ControllerSupportArgV7
{ {
public ControllerSupportArgHeader Header; public ControllerSupportArgHeader Header;
public fixed uint IdentificationColor[8]; public Array8<uint> IdentificationColor;
public byte EnableExplainText; public byte EnableExplainText;
public fixed byte ExplainText[8 * 0x81]; public ExplainTextStruct ExplainText;
[StructLayout(LayoutKind.Sequential, Size = 8 * 0x81)]
public struct ExplainTextStruct
{
private byte element;
public Span<byte> ToSpan() => MemoryMarshal.CreateSpan(ref element, 8 * 0x81);
}
} }
#pragma warning restore CS0649 #pragma warning restore CS0649
} }

View file

@ -1,16 +1,26 @@
using Ryujinx.Common.Memory;
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Applets namespace Ryujinx.HLE.HOS.Applets
{ {
#pragma warning disable CS0649 #pragma warning disable CS0649
// (1.0.0+ version) // (1.0.0+ version)
[StructLayout(LayoutKind.Sequential, Pack=1)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct ControllerSupportArgVPre7 struct ControllerSupportArgVPre7
{ {
public ControllerSupportArgHeader Header; public ControllerSupportArgHeader Header;
public fixed uint IdentificationColor[4]; public Array4<uint> IdentificationColor;
public byte EnableExplainText; public byte EnableExplainText;
public fixed byte ExplainText[4 * 0x81]; public ExplainTextStruct ExplainText;
[StructLayout(LayoutKind.Sequential, Size = 4 * 0x81)]
public struct ExplainTextStruct
{
private byte element;
public Span<byte> ToSpan() => MemoryMarshal.CreateSpan(ref element, 4 * 0x81);
}
} }
#pragma warning restore CS0649 #pragma warning restore CS0649
} }

View file

@ -1,13 +1,14 @@
using Ryujinx.Common.Memory;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Applets namespace Ryujinx.HLE.HOS.Applets
{ {
#pragma warning disable CS0649 #pragma warning disable CS0649
[StructLayout(LayoutKind.Sequential, Pack=1)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct ControllerSupportResultInfo struct ControllerSupportResultInfo
{ {
public sbyte PlayerCount; public sbyte PlayerCount;
fixed byte _padding[3]; private Array3<byte> _padding;
public uint SelectedId; public uint SelectedId;
public uint Result; public uint Result;
} }

View file

@ -1,13 +1,15 @@
using System.Runtime.InteropServices; using Ryujinx.Common.Memory;
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Applets.Error namespace Ryujinx.HLE.HOS.Applets.Error
{ {
[StructLayout(LayoutKind.Sequential, Pack = 1)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct ApplicationErrorArg struct ApplicationErrorArg
{ {
public uint ErrorNumber; public uint ErrorNumber;
public ulong LanguageCode; public ulong LanguageCode;
public fixed byte MessageText[0x800]; public ByteArray2048 MessageText;
public fixed byte DetailsText[0x800]; public ByteArray2048 DetailsText;
} }
} }

View file

@ -34,8 +34,7 @@ namespace Ryujinx.HLE.HOS.Applets.Error
_horizon = horizon; _horizon = horizon;
} }
public ResultCode Start(AppletSession normalSession, public ResultCode Start(AppletSession normalSession, AppletSession interactiveSession)
AppletSession interactiveSession)
{ {
_normalSession = normalSession; _normalSession = normalSession;
_commonArguments = IApplet.ReadStruct<CommonArguments>(_normalSession.Pop()); _commonArguments = IApplet.ReadStruct<CommonArguments>(_normalSession.Pop());
@ -176,11 +175,9 @@ namespace Ryujinx.HLE.HOS.Applets.Error
byte[] messageTextBuffer = new byte[0x800]; byte[] messageTextBuffer = new byte[0x800];
byte[] detailsTextBuffer = new byte[0x800]; byte[] detailsTextBuffer = new byte[0x800];
unsafe
{ applicationErrorArg.MessageText.ToSpan().CopyTo(messageTextBuffer);
Marshal.Copy((IntPtr)applicationErrorArg.MessageText, messageTextBuffer, 0, 0x800); applicationErrorArg.DetailsText.ToSpan().CopyTo(detailsTextBuffer);
Marshal.Copy((IntPtr)applicationErrorArg.DetailsText, detailsTextBuffer, 0, 0x800);
}
string messageText = Encoding.ASCII.GetString(messageTextBuffer.TakeWhile(b => !b.Equals(0)).ToArray()); string messageText = Encoding.ASCII.GetString(messageTextBuffer.TakeWhile(b => !b.Equals(0)).ToArray());
string detailsText = Encoding.ASCII.GetString(detailsTextBuffer.TakeWhile(b => !b.Equals(0)).ToArray()); string detailsText = Encoding.ASCII.GetString(detailsTextBuffer.TakeWhile(b => !b.Equals(0)).ToArray());

View file

@ -19,8 +19,7 @@ namespace Ryujinx.HLE.HOS.Applets
_system = system; _system = system;
} }
public ResultCode Start(AppletSession normalSession, public ResultCode Start(AppletSession normalSession, AppletSession interactiveSession)
AppletSession interactiveSession)
{ {
_normalSession = normalSession; _normalSession = normalSession;
_interactiveSession = interactiveSession; _interactiveSession = interactiveSession;

View file

@ -66,8 +66,7 @@ namespace Ryujinx.HLE.HOS.Applets
_device = system.Device; _device = system.Device;
} }
public ResultCode Start(AppletSession normalSession, public ResultCode Start(AppletSession normalSession, AppletSession interactiveSession)
AppletSession interactiveSession)
{ {
lock (_lock) lock (_lock)
{ {

View file

@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
/// <summary> /// <summary>
/// A structure with configuration options of the software keyboard when starting a new input request in inline mode. /// A structure with configuration options of the software keyboard when starting a new input request in inline mode.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Pack=1, CharSet = CharSet.Unicode)] [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode)]
struct SoftwareKeyboardCalc struct SoftwareKeyboardCalc
{ {
public const int InputTextLength = SoftwareKeyboardCalcEx.InputTextLength; public const int InputTextLength = SoftwareKeyboardCalcEx.InputTextLength;

View file

@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
/// A structure with configuration options of the software keyboard when starting a new input request in inline mode. /// A structure with configuration options of the software keyboard when starting a new input request in inline mode.
/// This is the extended version of the structure with extended appear options. /// This is the extended version of the structure with extended appear options.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Pack=1, CharSet = CharSet.Unicode)] [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode)]
struct SoftwareKeyboardCalcEx struct SoftwareKeyboardCalcEx
{ {
/// <summary> /// <summary>

View file

@ -1,12 +1,14 @@
using Ryujinx.HLE.Ui; using Ryujinx.HLE.Ui;
using Ryujinx.Memory; using Ryujinx.Memory;
using System; using System;
using System.Buffers.Binary;
using System.Diagnostics; using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Drawing.Text; using System.Drawing.Text;
using System.IO; using System.IO;
using System.Numerics;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
@ -652,7 +654,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
DrawString(graphics, ControllerToggleText, _labelsTextFont, _textNormalBrush, labelPosition); DrawString(graphics, ControllerToggleText, _labelsTextFont, _textNormalBrush, labelPosition);
} }
private unsafe bool TryCopyTo(IVirtualMemoryManager destination, ulong position) private bool TryCopyTo(IVirtualMemoryManager destination, ulong position)
{ {
if (_surface == null) if (_surface == null)
{ {
@ -667,22 +669,20 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
// Convert the pixel format used in System.Drawing to the one required by a Switch Surface. // Convert the pixel format used in System.Drawing to the one required by a Switch Surface.
int dataLength = surfaceData.Stride * surfaceData.Height; int dataLength = surfaceData.Stride * surfaceData.Height;
byte* dataPointer = (byte*)surfaceData.Scan0;
byte* dataEnd = dataPointer + dataLength;
for (; dataPointer < dataEnd; dataPointer += 4) byte[] data = new byte[dataLength];
Span<uint> dataConvert = MemoryMarshal.Cast<byte, uint>(data);
Marshal.Copy(surfaceData.Scan0, data, 0, dataLength);
for (int i = 0; i < dataConvert.Length; i++)
{ {
*(uint*)dataPointer = (uint)( dataConvert[i] = BitOperations.RotateRight(BinaryPrimitives.ReverseEndianness(dataConvert[i]), 8);
(*(dataPointer + 0) << 16) |
(*(dataPointer + 1) << 8 ) |
(*(dataPointer + 2) << 0 ) |
(*(dataPointer + 3) << 24));
} }
try try
{ {
Span<byte> dataSpan = new Span<byte>((void*)surfaceData.Scan0, dataLength); destination.Write(position, data);
destination.Write(position, dataSpan);
} }
finally finally
{ {

View file

@ -328,17 +328,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
private void UpdateUnusedInputIfNotEqual(ref RingLifo<NpadCommonState> currentlyUsed, ref RingLifo<NpadCommonState> possiblyUnused) private void UpdateUnusedInputIfNotEqual(ref RingLifo<NpadCommonState> currentlyUsed, ref RingLifo<NpadCommonState> possiblyUnused)
{ {
bool isEquals; if (!Unsafe.AreSame(ref currentlyUsed, ref possiblyUnused))
unsafe
{
var aPointer = Unsafe.AsPointer(ref currentlyUsed);
var bPointer = Unsafe.AsPointer(ref possiblyUnused);
isEquals = aPointer == bPointer;
}
if (!isEquals)
{ {
NpadCommonState newState = new NpadCommonState(); NpadCommonState newState = new NpadCommonState();
@ -357,17 +347,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
private void UpdateUnusedSixInputIfNotEqual(ref RingLifo<SixAxisSensorState> currentlyUsed, ref RingLifo<SixAxisSensorState> possiblyUnused) private void UpdateUnusedSixInputIfNotEqual(ref RingLifo<SixAxisSensorState> currentlyUsed, ref RingLifo<SixAxisSensorState> possiblyUnused)
{ {
bool isEquals; if (!Unsafe.AreSame(ref currentlyUsed, ref possiblyUnused))
unsafe
{
var aPointer = Unsafe.AsPointer(ref currentlyUsed);
var bPointer = Unsafe.AsPointer(ref possiblyUnused);
isEquals = aPointer == bPointer;
}
if (!isEquals)
{ {
SixAxisSensorState newState = new SixAxisSensorState(); SixAxisSensorState newState = new SixAxisSensorState();

View file

@ -1,4 +1,5 @@
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
{ {
@ -30,13 +31,16 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
public NpadBatteryLevel BatteryLevelJoyRight; public NpadBatteryLevel BatteryLevelJoyRight;
public uint AppletFooterUiAttributes; public uint AppletFooterUiAttributes;
public AppletFooterUiType AppletFooterUiType; public AppletFooterUiType AppletFooterUiType;
private unsafe fixed byte _reserved2[0x7B]; private Reserved2Struct _reserved2;
public RingLifo<NpadGcTriggerState> GcTrigger; public RingLifo<NpadGcTriggerState> GcTrigger;
public NpadLarkType LarkTypeLeftAndMain; public NpadLarkType LarkTypeLeftAndMain;
public NpadLarkType LarkTypeRight; public NpadLarkType LarkTypeRight;
public NpadLuciaType LuciaType; public NpadLuciaType LuciaType;
public uint Unknown43EC; public uint Unknown43EC;
[StructLayout(LayoutKind.Sequential, Size = 123, Pack = 1)]
private struct Reserved2Struct {}
public static NpadInternalState Create() public static NpadInternalState Create()
{ {
return new NpadInternalState return new NpadInternalState

View file

@ -59,23 +59,23 @@ namespace Ryujinx.HLE.HOS.Services.Ro
{ {
return ResultCode.InvalidNrr; return ResultCode.InvalidNrr;
} }
else if (header.NrrSize != nrrSize) else if (header.Size != nrrSize)
{ {
return ResultCode.InvalidSize; return ResultCode.InvalidSize;
} }
List<byte[]> hashes = new List<byte[]>(); List<byte[]> hashes = new List<byte[]>();
for (int i = 0; i < header.HashCount; i++) for (int i = 0; i < header.HashesCount; i++)
{ {
byte[] temp = new byte[0x20]; byte[] hash = new byte[0x20];
_owner.CpuMemory.Read(nrrAddress + header.HashOffset + (uint)(i * 0x20), temp); _owner.CpuMemory.Read(nrrAddress + header.HashesOffset + (uint)(i * 0x20), hash);
hashes.Add(temp); hashes.Add(hash);
} }
nrrInfo = new NrrInfo((ulong)nrrAddress, header, hashes); nrrInfo = new NrrInfo(nrrAddress, header, hashes);
return ResultCode.Success; return ResultCode.Success;
} }

View file

@ -1,23 +1,15 @@
using Ryujinx.Common.Memory;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Ro namespace Ryujinx.HLE.HOS.Services.Ro
{ {
[StructLayout(LayoutKind.Explicit, Size = 0x220)] [StructLayout(LayoutKind.Sequential, Size = 0x220)]
unsafe struct NRRCertification struct NRRCertification
{ {
[FieldOffset(0)]
public ulong ApplicationIdMask; public ulong ApplicationIdMask;
[FieldOffset(0x8)]
public ulong ApplicationIdPattern; public ulong ApplicationIdPattern;
private Array16<byte> _reserved;
[FieldOffset(0x10)] public ByteArray256 Modulus;
public fixed byte Reserved[0x10]; public ByteArray256 Signature;
[FieldOffset(0x20)]
public fixed byte Modulus[0x100];
[FieldOffset(0x120)]
public fixed byte Signature[0x100];
} }
} }

View file

@ -1,44 +1,22 @@
using Ryujinx.Common.Memory;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Ro namespace Ryujinx.HLE.HOS.Services.Ro
{ {
[StructLayout(LayoutKind.Explicit, Size = 0x350)] [StructLayout(LayoutKind.Sequential, Size = 0x350)]
unsafe struct NrrHeader struct NrrHeader
{ {
[FieldOffset(0)]
public uint Magic; public uint Magic;
public uint KeyGeneration; // 9.0.0+
[FieldOffset(0x4)] private Array8<byte> _reserved;
public uint CertificationSignatureKeyGeneration; // 9.0.0+
[FieldOffset(0x8)]
public ulong Reserved;
[FieldOffset(0x10)]
public NRRCertification Certification; public NRRCertification Certification;
public ByteArray256 Signature;
[FieldOffset(0x230)]
public fixed byte NrrSignature[0x100];
[FieldOffset(0x330)]
public ulong TitleId; public ulong TitleId;
public uint Size;
[FieldOffset(0x338)] public byte Kind; // 7.0.0+
public uint NrrSize; private Array3<byte> _reserved2;
public uint HashesOffset;
[FieldOffset(0x33C)] public uint HashesCount;
public byte Type; // 7.0.0+ private Array8<byte> _reserved3;
[FieldOffset(0x33D)]
public fixed byte Reserved2[0x3];
[FieldOffset(0x340)]
public uint HashOffset;
[FieldOffset(0x344)]
public uint HashCount;
[FieldOffset(0x348)]
public ulong Reserved3;
} }
} }

View file

@ -2,7 +2,6 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>