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
thread_func(void * arg)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
main(void)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