83 lines
3.4 KiB
C#
83 lines
3.4 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
using System.Globalization;
|
|
using System.Runtime.InteropServices;
|
|
using SharpEmu.HLE;
|
|
using Xunit;
|
|
|
|
namespace SharpEmu.Libs.Tests.Memory;
|
|
|
|
internal static unsafe class NativePageProtection
|
|
{
|
|
// Inspect the kernel state because direct mprotect calls bypass allocation records.
|
|
public static uint Query(ulong address)
|
|
{
|
|
if (OperatingSystem.IsLinux())
|
|
{
|
|
foreach (var line in File.ReadLines("/proc/self/maps"))
|
|
{
|
|
var separator = line.IndexOf('-');
|
|
var permissionStart = line.IndexOf(' ') + 1;
|
|
var start = ulong.Parse(line.AsSpan(0, separator), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
|
var end = ulong.Parse(line.AsSpan(separator + 1, permissionStart - separator - 2),
|
|
NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
|
if (address < start || address >= end) continue;
|
|
var permissions = line.AsSpan(permissionStart, 3);
|
|
return ConvertProtection(permissions[0] == 'r', permissions[1] == 'w', permissions[2] == 'x');
|
|
}
|
|
Assert.Fail("The address has no native mapping.");
|
|
}
|
|
else if (OperatingSystem.IsMacOS())
|
|
{
|
|
const int basicInformation64 = 9;
|
|
var regionAddress = address;
|
|
ulong regionSize = 0;
|
|
uint informationCount = 9;
|
|
var information = stackalloc int[9];
|
|
var task = MachTaskSelf();
|
|
var result = MachVmRegion(task, ref regionAddress, ref regionSize, basicInformation64,
|
|
information, ref informationCount, out var objectName);
|
|
try
|
|
{
|
|
Assert.Equal(0, result);
|
|
Assert.Equal(9u, informationCount);
|
|
Assert.True(regionAddress <= address && address - regionAddress < regionSize);
|
|
return ConvertProtection((information[0] & 1) != 0, (information[0] & 2) != 0,
|
|
(information[0] & 4) != 0);
|
|
}
|
|
finally
|
|
{
|
|
if (objectName != 0) Assert.Equal(0, MachPortDeallocate(task, objectName));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Assert.NotEqual((nuint)0, HostMemory.Query((void*)address, out var information));
|
|
return information.Protect & 0xFF;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
private static uint ConvertProtection(bool readable, bool writable, bool executable) =>
|
|
(readable, writable, executable) switch
|
|
{
|
|
(_, true, true) => HostMemory.PAGE_EXECUTE_READWRITE,
|
|
(true, false, true) => HostMemory.PAGE_EXECUTE_READ,
|
|
(false, false, true) => HostMemory.PAGE_EXECUTE,
|
|
(_, true, false) => HostMemory.PAGE_READWRITE,
|
|
(true, false, false) => HostMemory.PAGE_READONLY,
|
|
_ => HostMemory.PAGE_NOACCESS,
|
|
};
|
|
|
|
[DllImport("libSystem.dylib", EntryPoint = "mach_task_self")]
|
|
private static extern uint MachTaskSelf();
|
|
|
|
[DllImport("libSystem.dylib", EntryPoint = "mach_vm_region")]
|
|
private static extern int MachVmRegion(uint task, ref ulong address, ref ulong size, int flavor,
|
|
int* information, ref uint informationCount, out uint objectName);
|
|
|
|
[DllImport("libSystem.dylib", EntryPoint = "mach_port_deallocate")]
|
|
private static extern int MachPortDeallocate(uint task, uint name);
|
|
}
|