Someone in the Phoronix comments wondered why not just map it (the kernel-assisted trampoline memory page) executable and not writeable, to avoid the attack surface but be significantly faster. I also wonder exactly why, admittedly I haven’t read the mailing list thread entirely but purely working on theory I wonder what the advantages of working on page fault is. Less abusable gadgets in memory? More flexibility? Perhaps additional Spectre-type mitigations?
Currently the page must be writeable at some point in order to create the trampoline.
A page fault is used as a way of executing the trampoline without the page having to be made executable/writable---the page fault handler recognises the page as a special trampoline page and handles the jump to the trampolines target address (which was previously registered using the new syscall).
Note that AFAICS this is unrelated to Spectre. The intended use is for constructing closures for use in FFI libraries such as libffi.
EDIT: I think I perhaps misunderstood your query---are you saying why not just make a system call where the kernel creates the page for you with the desired trampoline code and execute permissions?
Double map it, like a JIT does. Once writable, and once executable. Put the pointers into different shared objects so that ASLR puts a randomized offset between them and you can't discover the write pointers from the execute pointer, and vice versa.
JavaScriptCore had an amusing scheme where they'd make an (executable) memcpy gadget with the address hardcoded, then throw away read permissions to that memory. So the pointer's address is thus not readable without modifying memory permissions.
Yes, the edit is precisely what I was trying to get at. Do you happen to have context around this?
edit: also, “spectre mitigations” was just a shot in the dark. It does feel like this mode of jmping (modifying saved registers and restoring them) would be more prone to interfering with speculative execution.
Its a good question, and one that seem to be unspoken.
I'm guessing, the charitable version could be that the implementation on a major processor vendor works out to be just the right amount of security because the page fault handler wasn't optimized as heavily as the syscall interface. So, this might provide better security on a "slower" path, which turns out to actually be faster once the faster path is fully secured?
It seems that it's common practice on phoronix to list the employer of the person submitting the patch. I found a few examples where Intel, Google, and Amazon are all mentioned
It's pretty common for Michael to add the affiliation/organization of people involved, since the social aspects are as interesting as the technical ones.
For example, since Alyssa Rosenzweig writes full-length articles about her and others' work on Mesa drivers for Mali GPUs, she is mentioned in article titles or in main paragraphs, or Collabora is mentioned.
Michael also covers interesting hires and organizational changes, like Valve hiring another developer for RADV this week, Valve is prominent in the title because it is important social information.
It is better than some random person giving idea on some project. If a Microsoft employee is proposing on behalf of the employer it has more chance of being proceeded forwarded/maintained/funded.
I think there are several slightly different variations, but generally a trampoline is something like this pseudo-code:
while (next_fn_to_call != nullptr) {
next_fn_to_call = next_fn_to_call()
}
So each function potentially wants to call a new function once it's done, but you don't want to blow the stack (or maybe use a stack at all) so it returns that function (pointer) rather than calling it. Then you need a little loop to call the return value: that's your trampoline.
The article is potentially a bit lower level given it's talking about things down at the CPU level, but I imagine its a vaguely connected idea.
Sounds a bit like how vsyscall works, except without kernel ABI reasons to back it up :/ Trying to have the kernel/application figure out if a trampoline is “legit” sounds like a bad idea, just like GCC’s nested functions in the first place (which people have been told to avoid for quite a while). Why not tell applications to migrate to iOS-style trampoline page remapping that’s used for imp_implementationWithBlock: https://landonf.org/code/objc/imp_implementationWithBlock.20...? libffi will likely be migrating away from its current technique to support Apple Silicon, anyhow.
Nested functions in c are totally awesome and perfectly safe as long as a) you don't pass them as function pointers and b) care about executable stacks. The nice thing is you avoid gross preprocessor crap.
When I was experimenting with threaded VM dispatch using computed gotos I discovered that GCC supports nested functions with __attribute__((constructor)). This hack allowed exporting computed goto addresses outside the execution loop function so that they'd be available when compiling bytecode.
This was useful because it meant your threaded jumps required fewer steps in the pipeline (compared to indexing a jump table), while also not needing any conditional for the threaded branch (e.g. not even checking for a NULL address before entering the loop, to initialize or substitute address placeholders).
The performance benefits of the constructor hack (low double digits, IIRC, maybe single digit, for a contrived test) were greater prior to Haswell (perhaps returning with Spectre mitigations?), but definitely not worth the headache of maintaining such non-portable, GCC-specific code. It did drive home, however, not only the usefulness of computed gotos (which are widely supported, excepting Visual Studio, and provide a far greater performance impact themselves), but the usefulness, in terms of design possibilities, of the ability to resolve computed goto addresses outside the function, which isn't supported anywhere except with that hack, AFAIK.
I guess I should monitor the libffi patch queue more carefully, as I only learned about their proposed changes by following the hacker news > phoronx > lkml rabbit hole.
38 comments
[ 3.1 ms ] story [ 87.0 ms ] threadA page fault is used as a way of executing the trampoline without the page having to be made executable/writable---the page fault handler recognises the page as a special trampoline page and handles the jump to the trampolines target address (which was previously registered using the new syscall).
Note that AFAICS this is unrelated to Spectre. The intended use is for constructing closures for use in FFI libraries such as libffi.
EDIT: I think I perhaps misunderstood your query---are you saying why not just make a system call where the kernel creates the page for you with the desired trampoline code and execute permissions?
edit: also, “spectre mitigations” was just a shot in the dark. It does feel like this mode of jmping (modifying saved registers and restoring them) would be more prone to interfering with speculative execution.
I'm guessing, the charitable version could be that the implementation on a major processor vendor works out to be just the right amount of security because the page fault handler wasn't optimized as heavily as the syscall interface. So, this might provide better security on a "slower" path, which turns out to actually be faster once the faster path is fully secured?
https://lkml.org/lkml/2020/7/28/640
"We're getting ignored in the mailing lists. What can we do about it?"
But the article is lazy and doesn't mention why it's important, so we're all going to infer something different about it.
[1] https://www.phoronix.com/scan.php?page=news_item&px=Intel-Bu...
[2] https://www.phoronix.com/scan.php?page=news_item&px=Google-Z...
[3] https://www.phoronix.com/scan.php?page=news_item&px=L1d-Cach...
For example, since Alyssa Rosenzweig writes full-length articles about her and others' work on Mesa drivers for Mali GPUs, she is mentioned in article titles or in main paragraphs, or Collabora is mentioned.
Michael also covers interesting hires and organizational changes, like Valve hiring another developer for RADV this week, Valve is prominent in the title because it is important social information.
If Microsoft is using the hours of the engineer’s workday for this purpose, it provides insight that does matter.
The article is potentially a bit lower level given it's talking about things down at the CPU level, but I imagine its a vaguely connected idea.
This was useful because it meant your threaded jumps required fewer steps in the pipeline (compared to indexing a jump table), while also not needing any conditional for the threaded branch (e.g. not even checking for a NULL address before entering the loop, to initialize or substitute address placeholders).
The performance benefits of the constructor hack (low double digits, IIRC, maybe single digit, for a contrived test) were greater prior to Haswell (perhaps returning with Spectre mitigations?), but definitely not worth the headache of maintaining such non-portable, GCC-specific code. It did drive home, however, not only the usefulness of computed gotos (which are widely supported, excepting Visual Studio, and provide a far greater performance impact themselves), but the usefulness, in terms of design possibilities, of the ability to resolve computed goto addresses outside the function, which isn't supported anywhere except with that hack, AFAIK.