Block/function-pointer bridge

184 views
Skip to first unread message

Michael Ash

unread,
Sep 17, 2010, 10:56:09 PM9/17/10
to cocoa-...@googlegroups.com
Well, I went and did it. Block signature metadata plus libffi closures equals function pointers that call through to blocks. Yahoo!

Code here:

http://github.com/mikeash/MABlockClosure

Unlike my previous insane hack in this area, this should be pretty portable and reliable due to using libffi and the signature metadata. It won't work on iOS, though, because you aren't allowed to generate new executable code there. Sorry.

Obviously your compiler needs to support generating the metadata, which right now means you have to use clang and not gcc. It'll blow up real quick if you use the wrong one, don't worry.

This should make the use of function-pointer callback APIs a LOT nicer. Enjoy!

Mike

Steven Degutis

unread,
Sep 17, 2010, 11:56:34 PM9/17/10
to cocoa-...@googlegroups.com
Kick. Ass. Nicely done Mike!

-Steven

Nathan de Vries

unread,
Sep 18, 2010, 5:01:10 AM9/18/10
to cocoa-...@googlegroups.com
Hey Mike,

This is really neat!

I wonder if we'll ever be able to mark memory as executable on iOS. Projects like MacRuby use libffi in the same way you do here, doomed to the same fate of not being usable on iOS.

Have you seen the way Tim Burks' Nu works around this problem on iOS? Nu creates a pool of IMPs at compile time, each of which calls an internal handler, and returns a specific type. In the case of Nu, the handler invokes the NuBlock associated with the IMP at runtime, but you could just as easily be invoking an Objective-C block.


Cheers,

Nathan de Vries

Michael Ash

unread,
Sep 18, 2010, 11:42:01 AM9/18/10
to cocoa-...@googlegroups.com
On Sep 18, 2010, at 5:01 AM, Nathan de Vries wrote:

> This is really neat!
>
> I wonder if we'll ever be able to mark memory as executable on iOS. Projects like MacRuby use libffi in the same way you do here, doomed to the same fate of not being usable on iOS.

Hard to say. If it's purely a "no interpreters" measure, Apple's new stance on this would indicate that it might go away. If it's a security thing, much less likely. Security seems to be the reasoning, alas.

> Have you seen the way Tim Burks' Nu works around this problem on iOS? Nu creates a pool of IMPs at compile time, each of which calls an internal handler, and returns a specific type. In the case of Nu, the handler invokes the NuBlock associated with the IMP at runtime, but you could just as easily be invoking an Objective-C block.

I have not seen this. That's an interesting approach, but I'd be afraid of any limited pool running out of entries at just the wrong time.

Mike

Nathan de Vries

unread,
Sep 18, 2010, 8:29:08 PM9/18/10
to cocoa-...@googlegroups.com
On 19/09/2010, at 1:42 AM, Michael Ash wrote:
> I have not seen this. That's an interesting approach, but I'd be afraid of any limited pool running out of entries at just the wrong time.

Indeed, the number of IMPs in the pool (1500) seems somewhat arbitrary. I wonder if there's any method to the madness?


Cheers,

Nathan de Vries

Michael Ash

unread,
Sep 19, 2010, 11:09:24 AM9/19/10
to cocoa-...@googlegroups.com

Maybe it fills a nice number of pages?

I came up with an idea where you compile trampolines that grab context data from an adjacent page. iOS doesn't let you mark data as executable, or modify executable data, but it does let you *duplicate* executable pages. When you need more trampolines, you duplicate the page with the trampolines, then stick modifiable context data next to that page. Landon Fuller and I (actually he's doing all the work) are trying to shift this from concept to reality now. If it works, my code (and anything else using libffi closures) should be able to work on iOS as well.

Mike

Tim

unread,
Sep 19, 2010, 2:26:29 PM9/19/10
to cocoa-unbound
Mike/Nathan,

The number of handlers in the Nu pools is completely arbitrary. The
main reason that I added them was to show that the mprotect policies
in iOS weren't effective at blocking Nu and similar scripting
languages. I guess they weren't enough to protect the PDF reader,
either.

Mike/Landon, if your page duplication scheme works, I think it would
be great to have it hidden it under the libffi API.

Tim

Michael Ash

unread,
Sep 19, 2010, 3:49:10 PM9/19/10
to cocoa-...@googlegroups.com
On Sep 19, 2010, at 2:26 PM, Tim wrote:

> The number of handlers in the Nu pools is completely arbitrary. The
> main reason that I added them was to show that the mprotect policies
> in iOS weren't effective at blocking Nu and similar scripting
> languages. I guess they weren't enough to protect the PDF reader,
> either.
>
> Mike/Landon, if your page duplication scheme works, I think it would
> be great to have it hidden it under the libffi API.

The work is coming along. The trampolines are generated and interfacing with the higher-level libffi code. Some work remains (allocating them, cleaning up the code to be sane, etc.) but the concept is sound. I'll update the list once we (Landon) have something usable. It would be great to allow Nu and other languages to take advantage too.

Mike

Michael Ash

unread,
Sep 19, 2010, 10:30:10 PM9/19/10
to cocoa-...@googlegroups.com
On Sep 19, 2010, at 2:26 PM, Tim wrote:

> Mike/Landon, if your page duplication scheme works, I think it would
> be great to have it hidden it under the libffi API.

It works! Landon completely reimplemented the libffi closure allocation API to use this technique, and I fixed up MABlockClosure to conditionally use that API instead of the less-capable closure initialization API (which is the only one available in Mac OS X's built-in libffi). All the tests pass on an actual physical iPhone and it appears to be at least vaguely solid.

Landon's fork is available here:

http://github.com/landonf/libffi-ios

MABlockClosure is updated to use that fork when targeting iOS, with some instructions for how to make it all work in the readme:

http://github.com/mikeash/MABlockClosure

In case anyone is interested, I'll go into a little more detail about how this whole crazy scheme works.

Normal libffi closures are a small chunk of trampoline code followed by a bit of context data. The trampoline code saves any registers that need saving, loads the context data using PC-relative loads, and then jumps off to a libffi function. That function then marshals all of the arguments passed and then uses the context data to invoke your closure callback.

Because the context is stored with the trampoline, and the context has to be generated at runtime, the page where the closure resides has to be written to when creating the closure. It then needs to be marked executable so that you can actually call it. This second step is not allowed on iOS, as we know.

The libffi-ios fork alters the nature of the trampoline by making it load context data from one page before the trampoline. This means that the trampoline page can remain constant. A full page of trampolines is built at compile time using assembly. At runtime, this trampoline page can be duplicated to different places in the address space without losing the executable bit. Some clever memory allocation places a read/write data page immediately before the duplicated trampoline page. The libffi calls can then alter the contents of this data page and thus alter the behavior of the individual trampolines without needing to actually alter any pages marked as executable. Since the trampoline page can be duplicated without limit (until you run out of address space), there is no hard limit on how many trampolines can be allocated at one time.

Since libffi-ios only changes the implementation of the standard libffi functions ffi_closure_alloc and ffi_closure_free, this fix can be easily used by any libffi code, and are not specific to MABlockClosure. Of course if you're doing something similar to what MABlockClosure does, consider taking advantage of its code.

I think that's it for now. If you have questions or run into difficulties, I'm happy to help.

Mike

Dylan Lukes

unread,
Sep 20, 2010, 7:29:29 AM9/20/10
to cocoa-unbound
That sounds like a good way to do things. Why does iOS not have a
mprotect type command though? How is memory marked as executable (by
the OS at runtime?)

Michael Ash

unread,
Sep 20, 2010, 11:09:58 AM9/20/10
to cocoa-...@googlegroups.com
On Sep 20, 2010, at 7:29 AM, Dylan Lukes wrote:

> That sounds like a good way to do things. Why does iOS not have a
> mprotect type command though? How is memory marked as executable (by
> the OS at runtime?)

I believe it's a security measure. It makes it harder for an attacker to get code onto the system, and also makes it harder to bypass code signing. (Otherwise you could write your own code loader/dynamic linker and load further code off disk or, gasp, the interwebs.)

The ability surely exists, but is not made available to us mere mortal third parties, which is why the OS can do it but we can't.

Mike

Landon Fuller

unread,
Sep 19, 2010, 6:09:58 PM9/19/10
to cocoa-...@googlegroups.com

The code needs some review -- and I'd eventually like to get it merged upstream -- but it's working, and can run Mike's MABlockClosure test cases on my iOS 4.0 devices. I've got the current version available via:
http://github.com/landonf/libffi-ios/

As long as you use the newer ffi_closure_alloc/ffi_closure_free APIs -- and don't try to do write to the returned (non-writable) code pointer -- it should work as expected.

As Mike mentioned, iOS allows executable pages to be remapped, but not written to; the implementation works by mapping a page of pre-compiled trampolines (using vm_remap()) to sit directly after a newly allocated writable config page containing per-trampoline context. The trampolines (12 bytes each) load their context via PC-relative addressing, ie:

stmfd sp!, {r0-r3}

// Load the context argument from the config page.
// This places the first usable config value at _ffi_closure_trampoline_table-4080
// This accounts for the above 4-byte stmfd instruction, plus 8 bytes constant when loading from pc.
ldr r0, [pc, #-4092]

// Load the jump address from the config page.
ldr pc, [pc, #-4092]

An arbitrary number of trampolines can be allocated; new pages are allocated/remapped as necessary, with 340 trampolines per allocated page.

I've attached a copy of the MABlockClosure project that'll run on an iOS device (libffi included). To build your own armv6/ios static library, there's a ./build-ios.sh script in the project that will drive autoconf/make.

-landonf

ffi_block_closure.zip

Louis Gerbarg

unread,
Sep 20, 2010, 8:18:30 PM9/20/10
to cocoa-...@googlegroups.com
On Sun, Sep 19, 2010 at 6:09 PM, Landon Fuller <lan...@plausible.coop> wrote:
>
> As Mike mentioned, iOS allows executable pages to be remapped, but not written to; the implementation works by mapping a page of pre-compiled trampolines (using vm_remap()) to sit directly after a newly allocated writable config page containing per-trampoline context. The trampolines (12 bytes each) load their context via PC-relative addressing, ie:
>

This is very cool, I had no idea the vm_remap allowed the copying of
executable pages without stripping the execute bit on the phone. I
actually have a use for this in a slightly different context (I am
optimizing the speed of dynamically generated accessor for some
objects). Rather than using libffi and double indirecting through a
trampoline in libffi I will probably just build my functions directly
into a page and then put their per instance static info into config
pages below them to avoid the extra jump in the speed path.

My one worry is whether or not this functionality is strictly an
oversight that can be turned off without breaking any extant code, or
if any parts of the system depend on it. The most likely candidates
are JavaScriptCore (depending on how much of the Cocoa bridge exists
under the hood), and CoreData (though it appears CoreData optimizes
its accessors by using per object vtables and grabbing some extra
space at the edge of the metaclass, which is what I had been planning
to do until I read this thread). If nothing Apple does actually remaps
executable pages then they may opt to break it to avoid people
targeting the config pages as an attack vector (which is not to say I
think it is a particularly viable attack vector).

Louis

Landon Fuller

unread,
Sep 24, 2010, 2:43:52 PM9/24/10
to cocoa-...@googlegroups.com

On Sep 20, 2010, at 5:18 PM, Louis Gerbarg wrote:

> My one worry is whether or not this functionality is strictly an
> oversight that can be turned off without breaking any extant code, or
> if any parts of the system depend on it.
> The most likely candidates are JavaScriptCore (depending on how much of the Cocoa bridge exists
> under the hood), and CoreData (though it appears CoreData optimizes
> its accessors by using per object vtables and grabbing some extra
> space at the edge of the metaclass, which is what I had been planning
> to do until I read this thread). If nothing Apple does actually remaps
> executable pages then they may opt to break it to avoid people
> targeting the config pages as an attack vector (which is not to say I
> think it is a particularly viable attack vector).

I only had a chance to look at it briefly, but the iOS CoreData framework seems to use the very same trick in _dynamicFunctionPointerFromPool(), with two different trampoline pages (__returnPointerCore and __accessorMethodCore):

- vm_allocate(mach_task_self(), 8192, ...) to reserve two full pages
- vm_deallocate(mach_task_self(), allocation+4096, 4096) to free the second page.
- vm_remap(mach_task_self(), ...)

One of the trampoline pages:

__returnPointerCore:
000ca000 46e6 mov lr, ip
000ca002 3904 subs r1, #4
000ca004 0b08 lsrs r0, r1, #12
000ca006 3801 subs r0, #1
000ca008 0300 lsls r0, r0, #12
000ca00a 0509 lsls r1, r1, #20
000ca00c 0d49 lsrs r1, r1, #21
000ca00e 5840 ldr r0, [r0, r1]
000ca010 4770 bx lr
000ca012 46c0 nop (mov r8, r8)
000ca014 46c0 nop (mov r8, r8)
000ca016 46c0 nop (mov r8, r8)

// First trampoline
000ca018 4679 mov r1, pc
000ca01a 46f4 mov ip, lr
000ca01c f7fffff0 bl __returnPointerCore
...

-landonf

Reply all
Reply to author
Forward
0 new messages