Created
April 24, 2022 16:10
-
-
Save Verdagon/fe458483dc7d1d992d499c750b45c091 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdint.h> | |
#include <stdlib.h> | |
#include <setjmp.h> | |
_Thread_local void* current_coroutine_args = NULL; | |
size_t rotate_left(size_t x, int bits) { | |
return (x << bits) | (x >> ((sizeof(size_t) * 8) - bits)); | |
} | |
size_t rotate_right(size_t x, int bits) { | |
return (x >> bits) | (x << ((sizeof(size_t) * 8) - bits)); | |
} | |
typedef struct SubFuncArgs { | |
size_t yield_destination_scrambled; | |
} SubFuncArgs; | |
void subFunc() { | |
int y = 20; | |
printf("y address after switch: %p\n", &y); | |
jmp_buf* yield_destination = (jmp_buf*)(rotate_right(((SubFuncArgs*)current_coroutine_args)->yield_destination_scrambled, 17) ^ 0x1337133713371337); | |
printf("Jumping back to old stack! buf at: %p\n", yield_destination); | |
longjmp(*yield_destination, 1); | |
// Should be impossible to get here | |
fprintf(stderr, "Unreachable!\n"); | |
exit(1); | |
} | |
int main() { | |
printf("jump buf size: %lu\n", sizeof(jmp_buf)); | |
int x = 10; | |
printf("x address before switch: %p\n", &x); | |
char* newStack = (char*)malloc(8 * 1024 * 1024); | |
char* coroutine_stack_frame_top = newStack + 8 * 1024 * 1024; | |
printf("allocated new stack: %p to %p\n", newStack, coroutine_stack_frame_top); | |
jmp_buf yield_destination; | |
// current_coroutine_entry = subFunc; | |
size_t yield_destination_scrambled = | |
rotate_left(((size_t)(&yield_destination) ^ 0x1337133713371337), 17); | |
SubFuncArgs args = { yield_destination_scrambled }; | |
current_coroutine_args = &args; | |
printf("About to jump to %p, buf at: %p\n", coroutine_stack_frame_top, &yield_destination); | |
register void* old_stack_pointer = NULL; | |
__asm__ __volatile__( | |
#ifdef __x86_64__ | |
"movq %%rsp,%[rs]" | |
#elif __i386__ | |
"movl %%esp,%[rs]" | |
#elif __arm__ | |
// sp is an alias for r13 | |
"mov %%sp,%[rs]" | |
#endif | |
: [rs] "=r" (old_stack_pointer) | |
: /* no input */ | |
); | |
if (setjmp(yield_destination) == 0) { | |
// setjmp returns zero when we first just call it to save the location. | |
// Later on, someone might jump back into that if-condition (just after the setjmp call) | |
// but they'll supply something other than zero, so they wont get inside this block. | |
register char* top = coroutine_stack_frame_top; | |
register void(*funcToCall)() = subFunc; | |
asm volatile( | |
#ifdef __x86_64__ | |
//"mov " | |
"mov %[rs], %%rsp \n" | |
#elif __i386__ | |
"mov %[rs], %%esp \n" | |
#elif __arm__ | |
// sp is an alias for r13 | |
"mov %[rs], %%sp \n" | |
#endif | |
"call *%[bz] \n" | |
: [ rs ] "+r" (top), [ bz ] "+r" (funcToCall) :: | |
); | |
// Accessing any locals after this point seems to be a big mistake, | |
// because with -fomit-frame-pointer on, it will calculate those locals' | |
// addresses by offsetting from the stack pointer... which we just messed with. | |
// So, don't access any locals here. | |
// Don't call any functions, because they could be inlined, and access locals. | |
// In fact, don't do anything here. | |
// Don't even look over here. | |
// Put it all in the assembly above. | |
// Unreachable, all coroutines know to longjmp back. | |
return 1; | |
} else { | |
// Someone jumped to the saved location! | |
printf("got the longjmp!\n"); | |
} | |
printf("done with program!\n"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment