savestates on waterbox
This commit is contained in:
parent
95dbd7c20b
commit
2952ac24aa
Binary file not shown.
|
@ -25,7 +25,7 @@ namespace BizHawk.BizInvoke
|
|||
_dirtydata = (WriteDetectionStatus[])(object)new byte[GetPage(EndExclusive - 1) + 1];
|
||||
|
||||
_pal = OSTailoredCode.IsUnixHost
|
||||
? (IMemoryBlockPal)new MemoryBlockUnixPal(Start, Size)
|
||||
? (IMemoryBlockPal)new MemoryBlockLinuxPal(Start, Size)
|
||||
: new MemoryBlockWindowsPal(Start, Size);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using static BizHawk.BizInvoke.MemoryBlock;
|
||||
using static BizHawk.BizInvoke.POSIXLibC;
|
||||
|
||||
namespace BizHawk.BizInvoke
|
||||
{
|
||||
internal sealed unsafe class MemoryBlockLinuxPal : IMemoryBlockPal
|
||||
{
|
||||
/*
|
||||
Differences compared with MemoryBlockWindowsPal:
|
||||
1) Commit is handled by only mapping up to the commit size, and then expanding commit is handled by unmap + truncate + remap.
|
||||
So all unmanaged structures (including LinGuard) are always looking at the committed size, not total size.
|
||||
2) Because of sigaltstack, RW_Stack is not needed and is made to behave the same as regular write guarding.
|
||||
*/
|
||||
|
||||
/// <summary>handle returned by <see cref="memfd_create"/></summary>
|
||||
private int _fd = -1;
|
||||
private ulong _start;
|
||||
private ulong _size;
|
||||
private ulong _committedSize;
|
||||
private bool _active;
|
||||
|
||||
/// <summary>
|
||||
/// Reserve bytes to later be swapped in, but do not map them
|
||||
/// </summary>
|
||||
/// <param name="start">eventual mapped address</param>
|
||||
/// <param name="size"></param>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// failed to get file descriptor
|
||||
/// </exception>
|
||||
public MemoryBlockLinuxPal(ulong start, ulong size)
|
||||
{
|
||||
// Console.WriteLine($".ctor {start:x16} {size:x16}");
|
||||
_start = start;
|
||||
_size = size;
|
||||
_fd = memfd_create("MemoryBlockUnix", 1 /*MFD_CLOEXEC*/);
|
||||
if (_fd == -1)
|
||||
throw new InvalidOperationException($"{nameof(memfd_create)}() failed with error {Marshal.GetLastWin32Error()}");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_fd == -1)
|
||||
return;
|
||||
if (_active)
|
||||
{
|
||||
try
|
||||
{
|
||||
Deactivate();
|
||||
}
|
||||
catch
|
||||
{}
|
||||
}
|
||||
close(_fd);
|
||||
_fd = -1;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
~MemoryBlockLinuxPal()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
{
|
||||
if (_committedSize > 0)
|
||||
{
|
||||
var ptr = mmap(Z.US(_start), Z.UU(_committedSize),
|
||||
MemoryProtection.Read | MemoryProtection.Write | MemoryProtection.Execute,
|
||||
17, // MAP_SHARED | MAP_FIXED
|
||||
_fd, IntPtr.Zero);
|
||||
if (ptr != Z.US(_start))
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(mmap)}() failed with error {Marshal.GetLastWin32Error()}");
|
||||
}
|
||||
// Console.WriteLine($"Add {_start} {_committedSize}");
|
||||
if ((IntPtr)LinGuard.AddTripGuard(Z.UU(_start), Z.UU(_committedSize)) == IntPtr.Zero)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(LinGuard.AddTripGuard)}() returned NULL");
|
||||
}
|
||||
}
|
||||
_active = true;
|
||||
}
|
||||
|
||||
public void Deactivate()
|
||||
{
|
||||
if (_committedSize > 0)
|
||||
{
|
||||
var errorCode = munmap(Z.US(_start), Z.UU(_committedSize));
|
||||
if (errorCode != 0)
|
||||
throw new InvalidOperationException($"{nameof(munmap)}() failed with error {Marshal.GetLastWin32Error()}");
|
||||
// Console.WriteLine($"Remove {_start} {_committedSize}");
|
||||
if (!LinGuard.RemoveTripGuard(Z.UU(_start), Z.UU(_committedSize)))
|
||||
throw new InvalidOperationException($"{nameof(LinGuard.RemoveTripGuard)}() returned FALSE");
|
||||
}
|
||||
_active = false;
|
||||
}
|
||||
|
||||
public void Commit(ulong length)
|
||||
{
|
||||
// Console.WriteLine($"commit {length:x16}");
|
||||
Deactivate();
|
||||
var errorCode = ftruncate(_fd, Z.US(length));
|
||||
if (errorCode != 0)
|
||||
throw new InvalidOperationException($"{nameof(ftruncate)}() failed with error {Marshal.GetLastWin32Error()}");
|
||||
_committedSize = length;
|
||||
Activate();
|
||||
}
|
||||
|
||||
private static MemoryProtection ToMemoryProtection(Protection prot)
|
||||
{
|
||||
switch (prot)
|
||||
{
|
||||
case Protection.None:
|
||||
return MemoryProtection.None;
|
||||
case Protection.R:
|
||||
return MemoryProtection.Read;
|
||||
case Protection.RW:
|
||||
return MemoryProtection.Read | MemoryProtection.Write;
|
||||
case Protection.RX:
|
||||
return MemoryProtection.Read | MemoryProtection.Execute;
|
||||
case Protection.RW_Invisible:
|
||||
return MemoryProtection.Read | MemoryProtection.Write;
|
||||
case Protection.RW_Stack:
|
||||
// because of sigaltstack, LinGuard has no issues with readonly stacks and the special distinction that
|
||||
// the windows port draws between stack vs non stack memory is ignored here
|
||||
return MemoryProtection.Read;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(prot));
|
||||
}
|
||||
}
|
||||
|
||||
public void Protect(ulong start, ulong size, Protection prot)
|
||||
{
|
||||
// Console.WriteLine($"protect {start:x16} {size:x16} {prot}");
|
||||
var errorCode = mprotect(
|
||||
Z.US(start),
|
||||
Z.UU(size),
|
||||
ToMemoryProtection(prot)
|
||||
);
|
||||
if (errorCode != 0)
|
||||
throw new InvalidOperationException($"{nameof(mprotect)}() failed with error {Marshal.GetLastWin32Error()}!");
|
||||
}
|
||||
|
||||
public void GetWriteStatus(WriteDetectionStatus[] dest, Protection[] pagedata)
|
||||
{
|
||||
if (_committedSize > 0)
|
||||
{
|
||||
// Console.WriteLine($"Examine {_start} {_committedSize}");
|
||||
var p = (IntPtr)LinGuard.ExamineTripGuard(Z.UU(_start), Z.UU(_committedSize));
|
||||
if (p == IntPtr.Zero)
|
||||
throw new InvalidOperationException($"{nameof(LinGuard.ExamineTripGuard)}() returned NULL!");
|
||||
Marshal.Copy(p, (byte[])(object)dest, 0, (int)(_committedSize >> WaterboxUtils.PageShift));
|
||||
}
|
||||
}
|
||||
|
||||
public void SetWriteStatus(WriteDetectionStatus[] src)
|
||||
{
|
||||
if (_committedSize > 0)
|
||||
{
|
||||
// Console.WriteLine($"Examine {_start} {_committedSize}");
|
||||
var p = (IntPtr)LinGuard.ExamineTripGuard(Z.UU(_start), Z.UU(_committedSize));
|
||||
if (p == IntPtr.Zero)
|
||||
throw new InvalidOperationException($"{nameof(LinGuard.ExamineTripGuard)}() returned NULL!");
|
||||
Marshal.Copy((byte[])(object)src, 0, p, (int)(_committedSize >> WaterboxUtils.PageShift));
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe class LinGuard
|
||||
{
|
||||
/// <summary>
|
||||
/// Add write detection to an area of memory. Any page in the specified range that has CanChange
|
||||
/// set and triggers an access violation on write
|
||||
/// will be noted, set to read+write permissions, and execution will be continued.
|
||||
/// CALLER'S RESPONSIBILITY: All addresses are page aligned.
|
||||
/// CALLER'S RESPONSIBILITY: No other thread enters any LinGuard function, or trips any tracked page during this call.
|
||||
/// CALLER'S RESPONSIBILITY: Pages to be tracked are mprotected to R. Pages with write permission
|
||||
/// cause no issues, but they will not trip.
|
||||
/// </summary>
|
||||
/// <returns>The same information as ExamineTripGuard, or null on failure</returns>
|
||||
[DllImport("linguard.so")]
|
||||
public static extern WriteDetectionStatus* AddTripGuard(UIntPtr start, UIntPtr length);
|
||||
/// <summary>
|
||||
/// Remove write detection from the specified addresses.
|
||||
/// CALLER'S RESPONSIBILITY: All addresses are page aligned.
|
||||
/// CALLER'S RESPONSIBILITY: No other thread enters any LinGuard function, or trips any tracked guard page during this call.
|
||||
/// </summary>
|
||||
/// <returns>false on failure (usually, the address range did not match a known one)</returns>
|
||||
[DllImport("linguard.so")]
|
||||
public static extern bool RemoveTripGuard(UIntPtr start, UIntPtr length);
|
||||
/// <summary>
|
||||
/// Examines a previously installed guard page detection.
|
||||
/// CALLER'S RESPONSIBILITY: All addresses are page aligned.
|
||||
/// CALLER'S RESPONSIBILITY: No other thread enters any LinGuard function, or trips any tracked guard page during this call.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A pointer to an array of bytes, one byte for each memory page in the range. Caller should set CanChange on pages to
|
||||
/// observe, and read back DidChange to see if things changed.
|
||||
/// </returns>
|
||||
[DllImport("linguard.so")]
|
||||
public static extern WriteDetectionStatus* ExamineTripGuard(UIntPtr start, UIntPtr length);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using static BizHawk.BizInvoke.MemoryBlock;
|
||||
using static BizHawk.BizInvoke.POSIXLibC;
|
||||
|
||||
namespace BizHawk.BizInvoke
|
||||
{
|
||||
public sealed class MemoryBlockUnixPal : IMemoryBlockPal
|
||||
{
|
||||
/// <summary>handle returned by <see cref="memfd_create"/></summary>
|
||||
private int _fd = -1;
|
||||
private ulong _start;
|
||||
private ulong _size;
|
||||
private ulong _committedSize;
|
||||
|
||||
/// <summary>
|
||||
/// Reserve bytes to later be swapped in, but do not map them
|
||||
/// </summary>
|
||||
/// <param name="start">eventual mapped address</param>
|
||||
/// <param name="size"></param>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// failed to get file descriptor
|
||||
/// </exception>
|
||||
public MemoryBlockUnixPal(ulong start, ulong size)
|
||||
{
|
||||
// Console.WriteLine($".ctor {start:x16} {size:x16}");
|
||||
_start = start;
|
||||
_size = size;
|
||||
_fd = memfd_create("MemoryBlockUnix", 1 /*MFD_CLOEXEC*/);
|
||||
if (_fd == -1)
|
||||
throw new InvalidOperationException($"{nameof(memfd_create)}() failed with error {Marshal.GetLastWin32Error()}");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_fd == -1)
|
||||
return;
|
||||
close(_fd);
|
||||
_fd = -1;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
~MemoryBlockUnixPal()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
{
|
||||
if (_committedSize > 0)
|
||||
{
|
||||
var ptr = mmap(Z.US(_start), Z.UU(_committedSize),
|
||||
MemoryProtection.Read | MemoryProtection.Write | MemoryProtection.Execute,
|
||||
17, // MAP_SHARED | MAP_FIXED
|
||||
_fd, IntPtr.Zero);
|
||||
if (ptr != Z.US(_start))
|
||||
throw new InvalidOperationException($"{nameof(mmap)}() failed with error {Marshal.GetLastWin32Error()}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Deactivate()
|
||||
{
|
||||
if (_committedSize > 0)
|
||||
{
|
||||
var errorCode = munmap(Z.US(_start), Z.UU(_committedSize));
|
||||
if (errorCode != 0)
|
||||
throw new InvalidOperationException($"{nameof(munmap)}() failed with error {Marshal.GetLastWin32Error()}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Commit(ulong length)
|
||||
{
|
||||
// Console.WriteLine($"commit {length:x16}");
|
||||
Deactivate();
|
||||
var errorCode = ftruncate(_fd, Z.US(length));
|
||||
if (errorCode != 0)
|
||||
throw new InvalidOperationException($"{nameof(ftruncate)}() failed with error {Marshal.GetLastWin32Error()}");
|
||||
_committedSize = length;
|
||||
Activate();
|
||||
}
|
||||
|
||||
public void Protect(ulong start, ulong size, Protection prot)
|
||||
{
|
||||
// Console.WriteLine($"protect {start:x16} {size:x16} {prot}");
|
||||
var errorCode = mprotect(
|
||||
Z.US(start),
|
||||
Z.UU(size),
|
||||
prot.ToMemoryProtection()
|
||||
);
|
||||
if (errorCode != 0)
|
||||
throw new InvalidOperationException($"{nameof(mprotect)}() failed with error {Marshal.GetLastWin32Error()}!");
|
||||
}
|
||||
|
||||
public void GetWriteStatus(WriteDetectionStatus[] dest, Protection[] pagedata)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
public void SetWriteStatus(WriteDetectionStatus[] src)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ namespace BizHawk.BizInvoke
|
|||
private IntPtr _handle;
|
||||
private ulong _start;
|
||||
private ulong _size;
|
||||
private bool _guardActive;
|
||||
private bool _active;
|
||||
|
||||
/// <summary>
|
||||
/// Reserve bytes to later be swapped in, but do not map them
|
||||
|
@ -54,7 +54,7 @@ namespace BizHawk.BizInvoke
|
|||
{
|
||||
throw new InvalidOperationException($"{nameof(WinGuard.AddTripGuard)}() returned NULL");
|
||||
}
|
||||
_guardActive = true;
|
||||
_active = true;
|
||||
}
|
||||
|
||||
public void Deactivate()
|
||||
|
@ -63,7 +63,7 @@ namespace BizHawk.BizInvoke
|
|||
throw new InvalidOperationException($"{nameof(Kernel32.UnmapViewOfFile)}() returned NULL");
|
||||
if (!WinGuard.RemoveTripGuard(Z.UU(_start), Z.UU(_size)))
|
||||
throw new InvalidOperationException($"{nameof(WinGuard.RemoveTripGuard)}() returned FALSE");
|
||||
_guardActive = false;
|
||||
_active = false;
|
||||
}
|
||||
|
||||
public void Protect(ulong start, ulong size, Protection prot)
|
||||
|
@ -101,13 +101,17 @@ namespace BizHawk.BizInvoke
|
|||
{
|
||||
if (_handle != IntPtr.Zero)
|
||||
{
|
||||
if (_active)
|
||||
{
|
||||
try
|
||||
{
|
||||
Deactivate();
|
||||
}
|
||||
catch
|
||||
{}
|
||||
}
|
||||
Kernel32.CloseHandle(_handle);
|
||||
_handle = IntPtr.Zero;
|
||||
if (_guardActive)
|
||||
{
|
||||
WinGuard.RemoveTripGuard(Z.UU(_start), Z.UU(_size));
|
||||
_guardActive = false;
|
||||
}
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,19 +31,5 @@ namespace BizHawk.BizInvoke
|
|||
/// <remarks>32-bit signed int</remarks>
|
||||
[Flags]
|
||||
public enum MemoryProtection : int { None = 0x0, Read = 0x1, Write = 0x2, Execute = 0x4 }
|
||||
|
||||
public static MemoryProtection ToMemoryProtection(this Protection prot)
|
||||
{
|
||||
switch (prot)
|
||||
{
|
||||
case Protection.None: return MemoryProtection.None;
|
||||
case Protection.R: return MemoryProtection.Read | MemoryProtection.Write; // FIXME
|
||||
case Protection.RW: return MemoryProtection.Read | MemoryProtection.Write;
|
||||
case Protection.RX: return MemoryProtection.Read | MemoryProtection.Execute;
|
||||
case Protection.RW_Invisible: return MemoryProtection.Read | MemoryProtection.Write;
|
||||
case Protection.RW_Stack: return MemoryProtection.Read | MemoryProtection.Write; // FIXME
|
||||
default: throw new ArgumentOutOfRangeException(nameof(prot));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
ifeq (,$(findstring Linux,$(shell uname)))
|
||||
$(error This must be built with linux)
|
||||
endif
|
||||
|
||||
linguard.so: linguard.c
|
||||
gcc -Wall -O2 -s -o $@ $< -shared -fPIC
|
||||
|
||||
install: linguard.so
|
||||
cp $< ../../output
|
||||
clean:
|
||||
rm linguard.so
|
||||
|
||||
.PHONY: install clean
|
|
@ -0,0 +1,279 @@
|
|||
#define _GNU_SOURCE
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define MAX_TRIPS 64
|
||||
|
||||
typedef struct {
|
||||
uintptr_t start;
|
||||
uintptr_t length;
|
||||
uint8_t tripped[0];
|
||||
} tripwire_t;
|
||||
|
||||
static tripwire_t* Trips[MAX_TRIPS];
|
||||
static int HandlerInstalled;
|
||||
|
||||
static char altstack[SIGSTKSZ];
|
||||
|
||||
static struct sigaction sa_old;
|
||||
|
||||
static void SignalHandler(int sig, siginfo_t* info, void* ucontext)
|
||||
{
|
||||
uintptr_t faultAddress = (uintptr_t)info->si_addr;
|
||||
for (int i = 0; i < MAX_TRIPS; i++)
|
||||
{
|
||||
if (Trips[i] && faultAddress >= Trips[i]->start && faultAddress < Trips[i]->start + Trips[i]->length)
|
||||
{
|
||||
uintptr_t page = (faultAddress - Trips[i]->start) >> 12;
|
||||
if (Trips[i]->tripped[page] & 1) // should change
|
||||
{
|
||||
if (mprotect((void*)(faultAddress & ~0xffful), 0x1000, PROT_READ | PROT_WRITE) != 0)
|
||||
{
|
||||
abort();
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
Trips[i]->tripped[page] = 3; // did change
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sa_old.sa_flags & SA_SIGINFO)
|
||||
sa_old.sa_sigaction(sig, info, ucontext);
|
||||
else
|
||||
sa_old.sa_handler(sig);
|
||||
}
|
||||
|
||||
static int InstallHandler()
|
||||
{
|
||||
stack_t ss;
|
||||
ss.ss_flags = 0;
|
||||
ss.ss_sp = altstack;
|
||||
ss.ss_size = sizeof(altstack);
|
||||
|
||||
if (sigaltstack(&ss, NULL) != 0)
|
||||
{
|
||||
fprintf(stderr, "sigaltstack: %i\n", errno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sigaction sa;
|
||||
sa.sa_sigaction = SignalHandler;
|
||||
sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
|
||||
sigfillset(&sa.sa_mask);
|
||||
|
||||
if (sigaction(SIGSEGV, &sa, &sa_old) != 0)
|
||||
{
|
||||
fprintf(stderr, "sigaction: %i\n", errno);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t* AddTripGuard(uintptr_t start, uintptr_t length)
|
||||
{
|
||||
if (!HandlerInstalled)
|
||||
{
|
||||
if (!InstallHandler())
|
||||
return NULL;
|
||||
HandlerInstalled = 1;
|
||||
}
|
||||
|
||||
uintptr_t npage = length >> 12;
|
||||
for (int i = 0; i < MAX_TRIPS; i++)
|
||||
{
|
||||
if (!Trips[i])
|
||||
{
|
||||
Trips[i] = calloc(1, sizeof(*Trips[i]) + npage);
|
||||
if (!Trips[i])
|
||||
return NULL;
|
||||
Trips[i]->start = start;
|
||||
Trips[i]->length = length;
|
||||
return &Trips[i]->tripped[0];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int64_t RemoveTripGuard(uintptr_t start, uintptr_t length)
|
||||
{
|
||||
for (int i = 0; i < MAX_TRIPS; i++)
|
||||
{
|
||||
if (Trips[i] && Trips[i]->start == start && Trips[i]->length == length)
|
||||
{
|
||||
free(Trips[i]);
|
||||
Trips[i] = NULL;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t* ExamineTripGuard(uintptr_t start, uintptr_t length)
|
||||
{
|
||||
for (int i = 0; i < MAX_TRIPS; i++)
|
||||
{
|
||||
if (Trips[i] && Trips[i]->start == start && Trips[i]->length == length)
|
||||
return &Trips[i]->tripped[0];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <linux/userfaultfd.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
struct uffdio_writeprotect wp;
|
||||
wp.range.start = start;
|
||||
wp.range.len = length;
|
||||
wp.mode = 0;
|
||||
if (ioctl(fd, UFFDIO_WRITEPROTECT, &wp) == -1)
|
||||
{
|
||||
free(Trips[i]);
|
||||
Trips[i] = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void v(const char* msg, int value)
|
||||
{
|
||||
if (v < 0)
|
||||
{
|
||||
printf("ERROR: %s %i\n", msg, errno);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const uint64_t addr = 0x36f00000000ul;
|
||||
const uint64_t size = 0x100000ul;
|
||||
|
||||
static void* threadproc(void* arg)
|
||||
{
|
||||
int wpfd = (int)(long)arg;
|
||||
// should be able to register once for a large range,
|
||||
// then wp smaller ranges
|
||||
struct uffdio_register uffdio_register;
|
||||
uffdio_register.range.start = addr;
|
||||
uffdio_register.range.len = size;
|
||||
|
||||
uffdio_register.mode = UFFDIO_REGISTER_MODE_WP;
|
||||
|
||||
v("ioctl:UFFDIO_REGISTER", ioctl(wpfd, UFFDIO_REGISTER, &uffdio_register));
|
||||
v("uffdio_register.ioctls", (uffdio_register.ioctls & UFFD_API_RANGE_IOCTLS) == UFFD_API_RANGE_IOCTLS ? 0 : -1);
|
||||
|
||||
// wp
|
||||
{
|
||||
struct uffdio_writeprotect wp;
|
||||
wp.range.start = addr;
|
||||
wp.range.len = size;
|
||||
wp.mode = UFFDIO_WRITEPROTECT_MODE_WP;
|
||||
v("ioctl:UFFDIO_WRITEPROTECT", ioctl(wpfd, UFFDIO_WRITEPROTECT, &wp));
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
struct uffd_msg msg;
|
||||
|
||||
int nb = read(wpfd, &msg, sizeof(msg));
|
||||
if (nb == -1)
|
||||
{
|
||||
if (errno == EAGAIN)
|
||||
continue;
|
||||
v("read", errno);
|
||||
}
|
||||
if (nb != sizeof(msg))
|
||||
v("sizeof(msg)", -1);
|
||||
if (msg.event & UFFD_EVENT_PAGEFAULT)
|
||||
{
|
||||
printf("==> Event is pagefault on %p flags 0x%llx write? 0x%llx wp? 0x%llx\n"
|
||||
, (void *)msg.arg.pagefault.address
|
||||
, msg.arg.pagefault.flags
|
||||
, msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WRITE
|
||||
, msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WP
|
||||
);
|
||||
}
|
||||
if (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WP)
|
||||
{
|
||||
// send write unlock
|
||||
struct uffdio_writeprotect wp;
|
||||
wp.range.start = addr;
|
||||
wp.range.len = size;
|
||||
wp.mode = 0;
|
||||
printf("sending !UFFDIO_WRITEPROTECT event to userfaultfd\n");
|
||||
v("ioctl:UFFDIO_WRITEPROTECT", ioctl(wpfd, UFFDIO_WRITEPROTECT, &wp));
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int fd = memfd_create("pewps", MFD_CLOEXEC);
|
||||
v("memfd_create", fd);
|
||||
printf("fd: %i\n", fd);
|
||||
v("ftruncate", ftruncate(fd, size));
|
||||
char* ptr = mmap((void*)addr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_FIXED, fd, 0);
|
||||
if (ptr == MAP_FAILED || ptr != (void*)addr)
|
||||
v("mmap", (int)(long)ptr);
|
||||
v("mprotect", mprotect(ptr, size, PROT_READ | PROT_WRITE));
|
||||
|
||||
int wpfd = syscall(SYS_userfaultfd, O_CLOEXEC);
|
||||
v("SYS_userfaultfd", wpfd);
|
||||
|
||||
struct uffdio_api uffdio_api;
|
||||
uffdio_api.api = UFFD_API;
|
||||
uffdio_api.features = 0;
|
||||
v("ioctl:UFFDIO_API", ioctl(wpfd, UFFDIO_API, &uffdio_api));
|
||||
|
||||
v("uffdio_api.api", uffdio_api.api == UFFD_API ? 0 : -1);
|
||||
|
||||
|
||||
pthread_t thr;
|
||||
v("pthread_create", pthread_create(&thr, NULL, threadproc, (void*)(long)wpfd));
|
||||
|
||||
for (uint64_t a = addr; a < addr + size; a += 4096)
|
||||
{
|
||||
sleep(1);
|
||||
printf("Gonna read");
|
||||
if (*(uint64_t*)a == 77777)
|
||||
{
|
||||
printf("Lucky!");
|
||||
}
|
||||
sleep(1);
|
||||
printf("Gonna write");
|
||||
strcpy((void*)a, "string in mem area");
|
||||
printf("%s\n", (char*)a);
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
Binary file not shown.
Loading…
Reference in New Issue