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 f32a6e5be0.

* 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 uint Padding;
public fixed byte LastUrl[0x1000];
public ByteArray4096 LastUrl;
public ulong LastUrlSize;
}
}

View file

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

View file

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

View file

@ -1,16 +1,26 @@
using Ryujinx.Common.Memory;
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Applets
{
#pragma warning disable CS0649
// (8.0.0+ version)
[StructLayout(LayoutKind.Sequential, Pack=1)]
unsafe struct ControllerSupportArgV7
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ControllerSupportArgV7
{
public ControllerSupportArgHeader Header;
public fixed uint IdentificationColor[8];
public Array8<uint> IdentificationColor;
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
}

View file

@ -1,16 +1,26 @@
using Ryujinx.Common.Memory;
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Applets
{
#pragma warning disable CS0649
// (1.0.0+ version)
[StructLayout(LayoutKind.Sequential, Pack=1)]
unsafe struct ControllerSupportArgVPre7
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ControllerSupportArgVPre7
{
public ControllerSupportArgHeader Header;
public fixed uint IdentificationColor[4];
public Array4<uint> IdentificationColor;
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
}

View file

@ -1,13 +1,14 @@
using Ryujinx.Common.Memory;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Applets
{
#pragma warning disable CS0649
[StructLayout(LayoutKind.Sequential, Pack=1)]
unsafe struct ControllerSupportResultInfo
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ControllerSupportResultInfo
{
public sbyte PlayerCount;
fixed byte _padding[3];
private Array3<byte> _padding;
public uint SelectedId;
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
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct ApplicationErrorArg
struct ApplicationErrorArg
{
public uint ErrorNumber;
public ulong LanguageCode;
public fixed byte MessageText[0x800];
public fixed byte DetailsText[0x800];
public uint ErrorNumber;
public ulong LanguageCode;
public ByteArray2048 MessageText;
public ByteArray2048 DetailsText;
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -59,23 +59,23 @@ namespace Ryujinx.HLE.HOS.Services.Ro
{
return ResultCode.InvalidNrr;
}
else if (header.NrrSize != nrrSize)
else if (header.Size != nrrSize)
{
return ResultCode.InvalidSize;
}
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;
}

View file

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

View file

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

View file

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