None, this is wildly undefined behavior, even considering GCC's extension and its documentation. I doubt that GCC implementers would be interested in defining the behavior here.
edit:
There might be room for some other extension here, but as an other commenter already wrote:
1. functions might not be contiguous in memory
2. the code that you are moving should better be position independent
So any such extension should handle these issues one way or an other.
Instead of if( condition) then code1: else code2, computed goto
will just switch the branch to unconditional jump to either code1/code2,
which bypasses branch misprediction penalty and is generally
more elegant.
It could be even worse. I've seen x86 cores where the icache/dcache consistency logic ended up internally being most of a page fault and recovery once noticed by the processor.
Oof on that note: at least in the example in the article, it's going to cost a TLB miss as page permissions are getting changed. So that's a page-table walk which means %cr3 -> PML4 -> PDPT -> PD -> PT -> the actual address you want to access; potentially up to 5 memory accesses (6 if your system has more than 256TB of RAM and you're using 5-level paging).
That being said, in a loop you'd only take this hit on the first run. You'd be back to cache misses after that.
> One of my research topics last year was self-modifying code mainly for *obfuscation*.
I think obfuscation is generally a really bad idea, but that was brought up in the article. One practical application for self-modifying code is hotpatching. Sometimes you really don't want to stop the process but want to change the implementation for some function.
The processor itself doesn't enforce W^X protection; even though writable pages are NX by convention, nothing inherently stops you from using (the equivalent of) mmap(PROT_READ | PROT_WRITE | PROT_EXEC) and copying the instructions in, or doing the same with existing pages using mprotect(), at which point you can hotpatch as much as you want. Of course, some OSes forbid this as a hardening measure, but allowing it remains very common.
You do the same thing JIT does and change the permissions on the page to NX, apply whatever update you want, then remove the write permission and make it executable again. Even though it seems like it doesn't make a difference, it's much more difficult for an attacker to change the page permissions than it is for the hotpatcher/JIT. I think this could still fail though depending on how strict your OS is in which case you might need to use explicit function pointers which are obviously writable.
Some systems apparently allow you to add executable without removing write as that is what the OP does with "mprotect(page, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC);". However,
Attackers love overflowing buffers but since that memory is not executable they can't put code in there directly. The JIT compiler is almost always the only component which writes to executable pages so they need to somehow trick it into emitting the code they want. It's my understanding that JIT spray attacks are harder to develop.
Computation itself can be seen as self-modifying. Browser loading JS, OS loading DLLs, closures, VMs. It's how "high up the abstraction" you want to go.
Right. It stopped being a useful optimization decades ago.
One of the few architectural mistakes von Neumann made back in 1945 is that he didn't think of index registers. He was addressing arrays by storing into the address part of instructions. By 1949, the index register (the "B Box") had been invented, and storing into instructions was mostly obsolete.
Would calculating algorithms that can be passed to a JIT-wise compiler callable directly, returning a pointer to object code that called can be called immediately count?
Turns out going from calculating an algorithm, optimized for whatever specialization is needed for performance, functionality & formats; to object code; to execution, with the simplest syntax imaginable, is pretty useful.
19 comments
[ 0.24 ms ] story [ 55.6 ms ] thread- hot patching
- making sure sysadmins have healthy sense of panic available to them at all times
edit: There might be room for some other extension here, but as an other commenter already wrote:
1. functions might not be contiguous in memory
2. the code that you are moving should better be position independent
So any such extension should handle these issues one way or an other.
That being said, in a loop you'd only take this hit on the first run. You'd be back to cache misses after that.
I think obfuscation is generally a really bad idea, but that was brought up in the article. One practical application for self-modifying code is hotpatching. Sometimes you really don't want to stop the process but want to change the implementation for some function.
Some systems apparently allow you to add executable without removing write as that is what the OP does with "mprotect(page, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC);". However,
I never got into this but "GNU lightning is a free-software library for generating assembly language code at run-time." https://en.wikipedia.org/wiki/GNU_lightning
https://onesphere.hashnode.dev/upstart
And for some reason: https://news.ycombinator.com/item?id=36976878
Right. It stopped being a useful optimization decades ago.
One of the few architectural mistakes von Neumann made back in 1945 is that he didn't think of index registers. He was addressing arrays by storing into the address part of instructions. By 1949, the index register (the "B Box") had been invented, and storing into instructions was mostly obsolete.
Turns out going from calculating an algorithm, optimized for whatever specialization is needed for performance, functionality & formats; to object code; to execution, with the simplest syntax imaginable, is pretty useful.
And fun
Cough, Forth, cough!