-
-
Save stuartcarnie/855607 to your computer and use it in GitHub Desktop.
// | |
// main.m | |
// ProtectTest | |
// Demonstrates newer versions of iOS now support PROT_EXEC pages, for just-in-time compilation. | |
// | |
// Must be compiled with Thumb disabled | |
// | |
// Created by Stuart Carnie on 3/4/11. | |
// Copyright 2011 Manomio LLC. All rights reserved. | |
// | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <errno.h> | |
#include <sys/mman.h> | |
#include <limits.h> /* for PAGESIZE */ | |
#ifndef PAGESIZE | |
#define PAGESIZE 4096 | |
#endif | |
typedef int (*inc_t)(int a); | |
inc_t _inc = NULL; | |
int | |
main(void) | |
{ | |
uint32_t code[] = { | |
0xe2800001, // add r0, r0, #1 | |
0xe12fff1e, // bx lr | |
}; | |
uint32_t *p; | |
/* Allocate a buffer; it will have the default | |
protection of PROT_READ|PROT_WRITE. */ | |
p = malloc(1024+PAGESIZE-1); | |
if (!p) { | |
perror("Couldn't malloc(1024)"); | |
exit(errno); | |
} | |
/* Align to a multiple of PAGESIZE, assumed to be a power of two */ | |
p = (uint32_t *)(((int) p + PAGESIZE-1) & ~(PAGESIZE-1)); | |
// copy instructions to function | |
p[0] = code[0]; | |
p[1] = code[1]; | |
/* Mark the buffer read / execute. */ | |
if (mprotect(p, 1024, PROT_READ | PROT_EXEC)) { | |
perror("Couldn't mprotect"); | |
exit(errno); | |
} | |
_inc = (inc_t)p; | |
int a = 1; | |
a = _inc(a); | |
printf("%d\n", a); // as expected, echos 2 | |
exit(0); | |
} |
It works on both iOS 5.1 and iOS 6.0 if you run it from Xcode. If you tap on the app it crashes in both cases. Presumably starting the app with gdbserver enables jitting (my guess is that this is to support executing random expressions in the debugger).
I was not able to set the "dynamic_codesign" entitlement, Xcode refused to deploy/install the app ("The executable was signed with invalid entitlements" and "The entitlements specified in your application's Code Signing Entitlements file do not match those specified in your provisioning profile.")
For future reference -
- You can technically change the protection of memory to be executable in iOS on local builds (you wouldn't get past app review process though, obviously).
- Marking memory regions for JIT use with 'mmap's MAP_JIT flag is still blocked, even in local builds. This is guarded today by the 'dynamic_codesign' entitlement. That's what's keeping JavaScriptCore.framework from using JIT when running locally in your app.
- It seems that the old method of just adding the dynamic_codesign entitlement in Xcode doesn't work anymore.
You can technically change the protection of memory to be executable in iOS on local builds (you wouldn't get past app review process though, obviously).
@dcaspi Could you elaborate on that? I have a small benchmark app I'm playing with for personal use (not going to the app store) that could benefit from executable memory. I've tried mmap and vm_protect but neither seems to work in giving me executable memory.
@vpeca You need a get-task-allow
entitlement (injected by Xcode automatically) and get ptrace
to trace you in some way. Either this can be done by attaching via LLDB, as Xcode does, or you can do it programmatically with ptrace(PT_TRACE_ME, 0, NULL, 0)
.
To follow up to @wtholliday's post, this does appear to work as is, without an entitlements, on iOS 6. Someone should check with Apple to see if this would actually be allowed into the App Store. If so, this is a very welcome change.