aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Memory
diff options
context:
space:
mode:
authorCaian Benedicto <caianbene@gmail.com>2021-02-28 07:48:04 -0300
committerGitHub <noreply@github.com>2021-02-28 11:48:04 +0100
commitf7b2daf5ecc20510fa9d6df87ea6166b5e49f230 (patch)
treee6564373f53191ae326d9bb894cbc1005ac60c67 /Ryujinx.Memory
parentfd9d16c3b3697ce2d9018a1636aa2a5d7aee3f80 (diff)
Fix virtual address overflow near ulong limit (#2044)
* Fix virtual address overflow near ulong limit * Fix comments * Improve overflow checking for large size values * Add overflow checking to AddressSpaceManager class * Add overflow protection to read and write functions
Diffstat (limited to 'Ryujinx.Memory')
-rw-r--r--Ryujinx.Memory/AddressSpaceManager.cs68
1 files changed, 59 insertions, 9 deletions
diff --git a/Ryujinx.Memory/AddressSpaceManager.cs b/Ryujinx.Memory/AddressSpaceManager.cs
index 09977bbd..916a3816 100644
--- a/Ryujinx.Memory/AddressSpaceManager.cs
+++ b/Ryujinx.Memory/AddressSpaceManager.cs
@@ -64,6 +64,8 @@ namespace Ryujinx.Memory
/// <param name="size">Size to be mapped</param>
public void Map(ulong va, ulong pa, ulong size)
{
+ AssertValidAddressAndSize(va, size);
+
while (size != 0)
{
PtMap(va, pa);
@@ -81,6 +83,8 @@ namespace Ryujinx.Memory
/// <param name="size">Size of the range to be unmapped</param>
public void Unmap(ulong va, ulong size)
{
+ AssertValidAddressAndSize(va, size);
+
while (size != 0)
{
PtUnmap(va);
@@ -138,6 +142,8 @@ namespace Ryujinx.Memory
return;
}
+ AssertValidAddressAndSize(va, (ulong)data.Length);
+
if (IsContiguousAndMapped(va, data.Length))
{
data.CopyTo(_backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length));
@@ -254,6 +260,23 @@ namespace Ryujinx.Memory
return ref _backingMemory.GetRef<T>(GetPhysicalAddressInternal(va));
}
+ /// <summary>
+ /// Computes the number of pages in a virtual address range.
+ /// </summary>
+ /// <param name="va">Virtual address of the range</param>
+ /// <param name="size">Size of the range</param>
+ /// <param name="startVa">The virtual address of the beginning of the first page</param>
+ /// <remarks>This function does not differentiate between allocated and unallocated pages.</remarks>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private int GetPagesCount(ulong va, uint size, out ulong startVa)
+ {
+ // WARNING: Always check if ulong does not overflow during the operations.
+ startVa = va & ~(ulong)PageMask;
+ ulong vaSpan = (va - startVa + size + PageMask) & ~(ulong)PageMask;
+
+ return (int)(vaSpan / PageSize);
+ }
+
private void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -262,16 +285,12 @@ namespace Ryujinx.Memory
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool IsContiguous(ulong va, int size)
{
- if (!ValidateAddress(va))
+ if (!ValidateAddress(va) || !ValidateAddressAndSize(va, (ulong)size))
{
return false;
}
- ulong endVa = (va + (ulong)size + PageMask) & ~(ulong)PageMask;
-
- va &= ~(ulong)PageMask;
-
- int pages = (int)((endVa - va) / PageSize);
+ int pages = GetPagesCount(va, (uint)size, out va);
for (int page = 0; page < pages - 1; page++)
{
@@ -310,6 +329,8 @@ namespace Ryujinx.Memory
return;
}
+ AssertValidAddressAndSize(va, (ulong)data.Length);
+
int offset = 0, size;
if ((va & PageMask) != 0)
@@ -362,11 +383,14 @@ namespace Ryujinx.Memory
return true;
}
- ulong endVa = (va + size + PageMask) & ~(ulong)PageMask;
+ if (!ValidateAddressAndSize(va, size))
+ {
+ return false;
+ }
- va &= ~(ulong)PageMask;
+ int pages = GetPagesCount(va, (uint)size, out va);
- while (va < endVa)
+ for (int page = 0; page < pages; page++)
{
if (!IsMapped(va))
{
@@ -385,6 +409,32 @@ namespace Ryujinx.Memory
}
/// <summary>
+ /// Checks if the combination of virtual address and size is part of the addressable space.
+ /// </summary>
+ /// <param name="va">Virtual address of the range</param>
+ /// <param name="size">Size of the range in bytes</param>
+ /// <returns>True if the combination of virtual address and size is part of the addressable space</returns>
+ private bool ValidateAddressAndSize(ulong va, ulong size)
+ {
+ ulong endVa = va + size;
+ return endVa >= va && endVa >= size && endVa <= _addressSpaceSize;
+ }
+
+ /// <summary>
+ /// Ensures the combination of virtual address and size is part of the addressable space.
+ /// </summary>
+ /// <param name="va">Virtual address of the range</param>
+ /// <param name="size">Size of the range in bytes</param>
+ /// <exception cref="InvalidMemoryRegionException">Throw when the memory region specified outside the addressable space</exception>
+ private void AssertValidAddressAndSize(ulong va, ulong size)
+ {
+ if (!ValidateAddressAndSize(va, size))
+ {
+ throw new InvalidMemoryRegionException($"va=0x{va:X16}, size=0x{size:X16}");
+ }
+ }
+
+ /// <summary>
/// Performs address translation of the address inside a mapped memory range.
/// </summary>
/// <remarks>