On 2011-04-07, at 3:44 PM, Maxime Chevalier-Boisvert wrote:
As I said, the code segment can be made read-write-execute with a
call to mprotect.
On which operating systems can you guarantee that this is feasible?
mprotect is in the same family of functions as mmap, which Tachyon currently uses to allocate machine code blocks. So it will very likely work on all the operating systems that we care about. Note that the equivalent function on WIN32 is called VirtualProtect. These facilities have existed for over 10 years in various operating systems.
I wrote the program attached below to verify that this technique works on the operating systems we care about most (GNU/Linux, Mac OS X, and Windows).
Marc
/* * File: "self-modif-code.c" * * Tests operating system support for self modifying code. * * Compile/execute: * * % gcc self-modif-code.c * % ./a.out * code_size = 16 * f(10) = 100 * 55 48 89 e5 89 7d fc 8b 45 fc 0f af 45 fc c9 c3 * c3 48 89 e5 89 7d fc 8b 45 fc 0f af 45 fc c9 c3 * f(10) = 10 * f(10) = 100 * * Correct operation has been verified on these operating systems: * * - GNU/Linux 2.6.27.25-78.2.56.fc9.i686d * - MacOS X 10.6.7 * - Windows XP and Windows 7 * */
#include <stdio.h> #include <stdlib.h> #include <stddef.h> #include <string.h>
typedef unsigned char u8;
int f(int x) { return x*x; } int f_end(int x) { return 0; }
#define MAX_CODE_SIZE 100
u8 *code = (u8*)f; u8 *code_end = (u8*)f_end; u8 code_copy[MAX_CODE_SIZE]; int code_size;
#if defined(linux) || defined(__linux) || defined(__linux__) #define USE_MPROTECT #endif
#if defined(__MACOSX__) || (defined(__APPLE__) && defined(__MACH__)) #define USE_MPROTECT #endif
#if defined(WIN32) || defined(_WIN32) #define USE_VIRTUALPROTECT #endif
#ifdef USE_MPROTECT #include <sys/mman.h> #endif
#ifdef USE_VIRTUALPROTECT #include <windows.h> #endif
void make_code_writable() { int page_size = 4096; ptrdiff_t a = ~(page_size-1) & (ptrdiff_t)code; ptrdiff_t b = ~(page_size-1) & (page_size-1+(ptrdiff_t)code_end);
#ifdef USE_MPROTECT
/* mprotect exists since 4.4 BSD circa 1994 */
mprotect((u8*)a, b-a, PROT_READ | PROT_WRITE | PROT_EXEC);
#endif
#ifdef USE_VIRTUALPROTECT
/* VirtualProtect exists since Windows 2000 circa 1999 */
DWORD old; VirtualProtect((u8*)a, b-a, PAGE_EXECUTE_READWRITE, &old);
#endif }
void print_code() { int i; for (i=0; i<25 && i<code_size; i++) printf("%02x ",code[i]); printf("\n"); }
int main() { code_size = code_end - code;
printf("code_size = %d\n", code_size);
if (code_size > MAX_CODE_SIZE) exit(1);
memcpy(code_copy, code, code_size);
printf("f(10) = %d\n", f(10));
print_code();
make_code_writable();
code[0] = 0xc3; /* x86 ret instruction */
print_code();
printf("f(10) = %d\n", f(10));
memcpy(code, code_copy, code_size);
printf("f(10) = %d\n", f(10));
return 0; }