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