From b4dc33efc2890bc0d60e99f715425d6af4a72b3d Mon Sep 17 00:00:00 2001 From: Mary Date: Sun, 24 Oct 2021 23:52:59 +0200 Subject: [PATCH] kernel: Clear pages allocated with SetHeapSize (#2776) * kernel: Clear pages allocated with SetHeapSize Before this commit, all new pages allocated by SetHeapSize were not cleared by the kernel. This would cause undefined data to be pass to the userland and possibly resulting in weird memory corruption. This commit also add support for custom fill heap and ipc value (that is also supported by the official kernel) * Remove dots at the end of KPageTableBase.MapPages new documentation * Remove unused _stackFillValue --- Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs | 14 +++++++++-- .../HOS/Kernel/Memory/KPageTableBase.cs | 24 ++++++++++++++----- .../HOS/Kernel/Memory/KPageTableHostMapped.cs | 20 +++++++++++++--- .../HOS/Kernel/Memory/MemoryFillValue.cs | 10 ++++++++ Ryujinx.Memory/MemoryBlock.cs | 9 +++---- 5 files changed, 62 insertions(+), 15 deletions(-) create mode 100644 Ryujinx.HLE/HOS/Kernel/Memory/MemoryFillValue.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs index 20a13f5769..d7ee04e329 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs @@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } /// - protected override KernelResult MapPages(ulong dstVa, ulong pagesCount, ulong srcPa, KMemoryPermission permission) + protected override KernelResult MapPages(ulong dstVa, ulong pagesCount, ulong srcPa, KMemoryPermission permission, bool shouldFillPages, byte fillValue) { ulong size = pagesCount * PageSize; @@ -99,11 +99,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory Context.MemoryManager.IncrementPagesReferenceCount(srcPa, pagesCount); } + if (shouldFillPages) + { + _cpuMemory.Fill(dstVa, size, fillValue); + } + return KernelResult.Success; } /// - protected override KernelResult MapPages(ulong address, KPageList pageList, KMemoryPermission permission) + protected override KernelResult MapPages(ulong address, KPageList pageList, KMemoryPermission permission, bool shouldFillPages, byte fillValue) { using var scopedPageList = new KScopedPageList(Context.MemoryManager, pageList); @@ -118,6 +123,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory _cpuMemory.Map(currentVa, Context.Memory.GetPointer(addr, size), size); + if (shouldFillPages) + { + _cpuMemory.Fill(currentVa, size, fillValue); + } + currentVa += size; } diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs index 965e03d9ec..ff87ecb79a 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs @@ -75,6 +75,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory public abstract bool SupportsMemoryAliasing { get; } + private MemoryFillValue _heapFillValue; + private MemoryFillValue _ipcFillValue; + public KPageTableBase(KernelContext context) { Context = context; @@ -82,6 +85,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory _blockManager = new KMemoryBlockManager(); _isKernel = false; + + _heapFillValue = MemoryFillValue.Zero; + _ipcFillValue = MemoryFillValue.Zero; } private static readonly int[] AddrSpaceSizes = new int[] { 32, 36, 32, 39 }; @@ -299,6 +305,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory TlsIoRegionStart = tlsIoRegion.Start; TlsIoRegionEnd = tlsIoRegion.End; + // TODO: Check kernel configuration via secure monitor call when implemented to set memory fill values. + _currentHeapAddr = HeapRegionStart; _heapCapacity = 0; PhysicalMemoryUsage = 0; @@ -738,7 +746,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return KernelResult.InvalidMemState; } - result = MapPages(_currentHeapAddr, pageList, KMemoryPermission.ReadAndWrite); + result = MapPages(_currentHeapAddr, pageList, KMemoryPermission.ReadAndWrite, true, (byte)_heapFillValue); if (result != KernelResult.Success) { @@ -1783,7 +1791,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { ulong unusedSizeBefore = address - addressTruncated; - Context.Memory.ZeroFill(GetDramAddressFromPa(dstFirstPagePa), unusedSizeBefore); + Context.Memory.Fill(GetDramAddressFromPa(dstFirstPagePa), unusedSizeBefore, (byte)_ipcFillValue); ulong copySize = addressRounded <= endAddr ? addressRounded - address : size; var data = srcPageTable.GetSpan(addressTruncated + unusedSizeBefore, (int)copySize); @@ -1801,7 +1809,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory if (unusedSizeAfter != 0) { - Context.Memory.ZeroFill(GetDramAddressFromPa(firstPageFillAddress), unusedSizeAfter); + Context.Memory.Fill(GetDramAddressFromPa(firstPageFillAddress), unusedSizeAfter, (byte)_ipcFillValue); } KernelResult result = MapPages(currentVa, 1, dstFirstPagePa, permission); @@ -1853,7 +1861,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory unusedSizeAfter = PageSize; } - Context.Memory.ZeroFill(GetDramAddressFromPa(lastPageFillAddr), unusedSizeAfter); + Context.Memory.Fill(GetDramAddressFromPa(lastPageFillAddr), unusedSizeAfter, (byte)_ipcFillValue); KernelResult result = MapPages(currentVa, 1, dstLastPagePa, permission); @@ -2779,8 +2787,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory /// Number of pages to map /// Physical address where the pages should be mapped. May be ignored if aliasing is not supported /// Permission of the region to be mapped + /// Indicate if the pages should be filled with the value + /// The value used to fill pages when is set to true /// Result of the mapping operation - protected abstract KernelResult MapPages(ulong dstVa, ulong pagesCount, ulong srcPa, KMemoryPermission permission); + protected abstract KernelResult MapPages(ulong dstVa, ulong pagesCount, ulong srcPa, KMemoryPermission permission, bool shouldFillPages = false, byte fillValue = 0); /// /// Maps a region of memory into the specified physical memory region. @@ -2788,8 +2798,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory /// Destination virtual address that should be mapped /// List of physical memory pages where the pages should be mapped. May be ignored if aliasing is not supported /// Permission of the region to be mapped + /// Indicate if the pages should be filled with the value + /// The value used to fill pages when is set to true /// Result of the mapping operation - protected abstract KernelResult MapPages(ulong address, KPageList pageList, KMemoryPermission permission); + protected abstract KernelResult MapPages(ulong address, KPageList pageList, KMemoryPermission permission, bool shouldFillPages = false, byte fillValue = 0); /// /// Maps a region of memory into the specified host memory ranges. diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableHostMapped.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableHostMapped.cs index cd51bab7c2..29a7b2ed6c 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableHostMapped.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableHostMapped.cs @@ -70,16 +70,30 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } /// - protected override KernelResult MapPages(ulong dstVa, ulong pagesCount, ulong srcPa, KMemoryPermission permission) + protected override KernelResult MapPages(ulong dstVa, ulong pagesCount, ulong srcPa, KMemoryPermission permission, bool shouldFillPages, byte fillValue) { _cpuMemory.Map(dstVa, 0, pagesCount * PageSize); + + if (shouldFillPages) + { + _cpuMemory.Fill(dstVa, pagesCount * PageSize, fillValue); + } + return KernelResult.Success; } /// - protected override KernelResult MapPages(ulong address, KPageList pageList, KMemoryPermission permission) + protected override KernelResult MapPages(ulong address, KPageList pageList, KMemoryPermission permission, bool shouldFillPages, byte fillValue) { - _cpuMemory.Map(address, 0, pageList.GetPagesCount() * PageSize); + ulong pagesCount = pageList.GetPagesCount(); + + _cpuMemory.Map(address, 0, pagesCount * PageSize); + + if (shouldFillPages) + { + _cpuMemory.Fill(address, pagesCount * PageSize, fillValue); + } + return KernelResult.Success; } diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/MemoryFillValue.cs b/Ryujinx.HLE/HOS/Kernel/Memory/MemoryFillValue.cs new file mode 100644 index 0000000000..cdc892fc59 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/Memory/MemoryFillValue.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.HLE.HOS.Kernel.Memory +{ + enum MemoryFillValue : byte + { + Zero = 0, + Stack = 0x58, + Ipc = 0x59, + Heap = 0x5A, + } +} diff --git a/Ryujinx.Memory/MemoryBlock.cs b/Ryujinx.Memory/MemoryBlock.cs index 25c66dfe37..3561b81a60 100644 --- a/Ryujinx.Memory/MemoryBlock.cs +++ b/Ryujinx.Memory/MemoryBlock.cs @@ -216,13 +216,14 @@ namespace Ryujinx.Memory } /// - /// Fills a region of memory with zeros. + /// Fills a region of memory with . /// - /// Offset of the region to fill with zeros + /// Offset of the region to fill with /// Size in bytes of the region to fill + /// Value to use for the fill /// Throw when the memory block has already been disposed /// Throw when either or are out of range - public void ZeroFill(ulong offset, ulong size) + public void Fill(ulong offset, ulong size, byte value) { const int MaxChunkSize = 1 << 24; @@ -230,7 +231,7 @@ namespace Ryujinx.Memory { int copySize = (int)Math.Min(MaxChunkSize, size - subOffset); - GetSpan(offset + subOffset, copySize).Fill(0); + GetSpan(offset + subOffset, copySize).Fill(value); } }