1 /* 2 * Test patching code, running in one thread, from another thread. 3 * 4 * Intel SDM calls this "cross-modifying code" and recommends a special 5 * sequence, which requires both threads to cooperate. 6 * 7 * Linux kernel uses a different sequence that does not require cooperation and 8 * involves patching the first byte with int3. 9 * 10 * Finally, there is user-mode software out there that simply uses atomics, and 11 * that seems to be good enough in practice. Test that QEMU has no problems 12 * with this as well. 13 */ 14 15 #include <assert.h> 16 #include <pthread.h> 17 #include <stdbool.h> 18 #include <stdlib.h> 19 20 void add1_or_nop(long *x); 21 asm(".pushsection .rwx,\"awx\",@progbits\n" 22 ".globl add1_or_nop\n" 23 /* addq $0x1,(%rdi) */ 24 "add1_or_nop: .byte 0x48, 0x83, 0x07, 0x01\n" 25 "ret\n" 26 ".popsection\n"); 27 28 #define THREAD_WAIT 0 29 #define THREAD_PATCH 1 30 #define THREAD_STOP 2 31 32 static void *thread_func(void *arg) 33 { 34 int val = 0x0026748d; /* nop */ 35 36 while (true) { 37 switch (__atomic_load_n((int *)arg, __ATOMIC_SEQ_CST)) { 38 case THREAD_WAIT: 39 break; 40 case THREAD_PATCH: 41 val = __atomic_exchange_n((int *)&add1_or_nop, val, 42 __ATOMIC_SEQ_CST); 43 break; 44 case THREAD_STOP: 45 return NULL; 46 default: 47 assert(false); 48 __builtin_unreachable(); 49 } 50 } 51 } 52 53 #define INITIAL 42 54 #define COUNT 1000000 55 56 int main(void) 57 { 58 int command = THREAD_WAIT; 59 pthread_t thread; 60 long x = 0; 61 int err; 62 int i; 63 64 err = pthread_create(&thread, NULL, &thread_func, &command); 65 assert(err == 0); 66 67 __atomic_store_n(&command, THREAD_PATCH, __ATOMIC_SEQ_CST); 68 for (i = 0; i < COUNT; i++) { 69 add1_or_nop(&x); 70 } 71 __atomic_store_n(&command, THREAD_STOP, __ATOMIC_SEQ_CST); 72 73 err = pthread_join(thread, NULL); 74 assert(err == 0); 75 76 assert(x >= INITIAL); 77 assert(x <= INITIAL + COUNT); 78 79 return EXIT_SUCCESS; 80 } 81