Hi,

when running a DynaRec version of RPCemu on an x86-64 Linux system or
an x86 one with the Physical Address Extension (PAE) enabled by the
kernel, it's possible that RPCemu instantly fails with a segmentation
fault when it attempts to execute DynaRec-generated code that resides
in memory pages marked as non-executable by default.

Distributions on which I've observed this include Fedora 7 and the
development version of Fedora 8, both using SELinux in enforcing mode.

Patch summary:

  * On x86 and x86-64 Linux, mark the memory pages containing DynaRec
    code blocks as executable. This prevents a segmentation fault on
    kernels that enable the CPU's NX/XD feature (No eXecute / eXecute
    Disable).

  (Authors: Christof Efkemann, Kai Thomsen)

On SELinux-using distributions like Fedora, it's also necessary to
actually allow RPCemu to mark any of its own memory pages as
executable, otherwise the mprotect() call fails with EPERM (permission
denied). On Fedora, you can toggle a particular SELinux boolean to
permit this operation for all processes:

  $ su -c "/usr/sbin/setsebool -P allow_execmem=1"

I realize that this compromises the system's security, but it's at
least better than disabling SELinux as a whole.

By the way, in Fedora's graphical configuration tool
`system-config-selinux', this SELinux boolean can be found under
"Memory Protection" -> "Allow unconfined executables to map a memory
region as both executable and writable. [...]".

Assuming the patch is OK to be committed, perhaps the preceding
paragraphs should be adapted for inclusion in readme.txt?

A clean solution that handles the RPCemu executable specifically would
be preferrable, of course.


--Kai
Index: codegen_amd64.c
===================================================================
--- codegen_amd64.c	(Revision 96)
+++ codegen_amd64.c	(Arbeitskopie)
@@ -7,6 +7,11 @@
 #include "mem.h"
 #include "arm.h"
 
+#ifdef __linux__
+#include <sys/mman.h>
+#include <unistd.h>
+#endif
+
 void generateupdatepc();
 int lastflagchange;
 unsigned char rcodeblock[BLOCKS][1792];
@@ -35,12 +40,29 @@
 void initcodeblocks()
 {
         int c;
+#ifdef __linux__
+	void *start;
+	size_t len;
+	long pagesize = sysconf(_SC_PAGESIZE);
+	long pagemask = ~(pagesize - 1);
+#endif
         /*Clear all blocks*/
         memset(codeblockpc,0xFF,4*0x1000);
 //        memset(codeblockcount,0,0x1000);
         blockpoint=0;
         for (c=0;c<BLOCKS;c++) blocks[c]=0xFFFFFFFF;
         for (c=0;c<BLOCKS;c++) codeblockaddr[c]=&rcodeblock[c][4];
+
+#ifdef __linux__
+	/* Set memory pages containing rcodeblock[]s executable -
+	   necessary when NX/XD feature is active on CPU(s) */
+	start = (void *)((long)rcodeblock & pagemask);
+	len = (sizeof rcodeblock + pagesize) & pagemask;
+	if (mprotect(start, len, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) {
+		perror("mprotect");
+		exit(1);
+	}
+#endif
 }
 
 void resetcodeblocks()
Index: codegen_x86.c
===================================================================
--- codegen_x86.c	(Revision 96)
+++ codegen_x86.c	(Arbeitskopie)
@@ -10,6 +10,11 @@
 #include "mem.h"
 #include "arm.h"
 
+#ifdef __linux__
+#include <sys/mman.h>
+#include <unistd.h>
+#endif
+
 void generateupdatepc();
 int linecyc;
 int hasldrb[BLOCKS];
@@ -63,6 +68,12 @@
 void initcodeblocks()
 {
         int c;
+#ifdef __linux__
+	void *start;
+	size_t len;
+	long pagesize = sysconf(_SC_PAGESIZE);
+	long pagemask = ~(pagesize - 1);
+#endif
         /*Clear all blocks*/
         memset(codeblockpc,0xFF,4*0x1000);
 //        memset(codeblockcount,0,0x1000);
@@ -205,6 +216,17 @@
                 addbyte(0x83); addbyte(0xC7); addbyte(4); /*ADDL $4,%edi*/
         }
         addbyte(0xC3); /*RET*/
+
+#ifdef __linux__
+	/* Set memory pages containing rcodeblock[]s executable -
+	   necessary when NX/XD feature is active on CPU(s) */
+	start = (void *)((long)rcodeblock & pagemask);
+	len = (sizeof rcodeblock + pagesize) & pagemask;
+	if (mprotect(start, len, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) {
+		perror("mprotect");
+		exit(1);
+	}
+#endif
 }
 
 void resetcodeblocks()
_______________________________________________
Rpcemu mailing list
[email protected]
http://www.riscos.info/cgi-bin/mailman/listinfo/rpcemu

Reply via email to