tests/tcg/x86_64: Add cross-modifying code test
commit f025692c99
("accel/tcg: Clear PAGE_WRITE before translation")
fixed cross-modifying code handling, but did not add a test. The
changed code was further improved recently [1], and I was not sure
whether these modifications were safe (spoiler: they were fine).
Add a test to make sure there are no regressions.
[1] https://lists.gnu.org/archive/html/qemu-devel/2022-09/msg00034.html
Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
Message-Id: <20241001150617.9977-1-iii@linux.ibm.com>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Message-Id: <20241023113406.1284676-8-alex.bennee@linaro.org>
This commit is contained in:
parent
7f117cbb46
commit
b6a48d2a4b
@ -17,6 +17,7 @@ X86_64_TESTS += cmpxchg
|
|||||||
X86_64_TESTS += adox
|
X86_64_TESTS += adox
|
||||||
X86_64_TESTS += test-1648
|
X86_64_TESTS += test-1648
|
||||||
X86_64_TESTS += test-2175
|
X86_64_TESTS += test-2175
|
||||||
|
X86_64_TESTS += cross-modifying-code
|
||||||
TESTS=$(MULTIARCH_TESTS) $(X86_64_TESTS) test-x86_64
|
TESTS=$(MULTIARCH_TESTS) $(X86_64_TESTS) test-x86_64
|
||||||
else
|
else
|
||||||
TESTS=$(MULTIARCH_TESTS)
|
TESTS=$(MULTIARCH_TESTS)
|
||||||
@ -27,6 +28,9 @@ adox: CFLAGS=-O2
|
|||||||
run-test-i386-ssse3: QEMU_OPTS += -cpu max
|
run-test-i386-ssse3: QEMU_OPTS += -cpu max
|
||||||
run-plugin-test-i386-ssse3-%: QEMU_OPTS += -cpu max
|
run-plugin-test-i386-ssse3-%: QEMU_OPTS += -cpu max
|
||||||
|
|
||||||
|
cross-modifying-code: CFLAGS+=-pthread
|
||||||
|
cross-modifying-code: LDFLAGS+=-pthread
|
||||||
|
|
||||||
test-x86_64: LDFLAGS+=-lm -lc
|
test-x86_64: LDFLAGS+=-lm -lc
|
||||||
test-x86_64: test-i386.c test-i386.h test-i386-shift.h test-i386-muldiv.h
|
test-x86_64: test-i386.c test-i386.h test-i386-shift.h test-i386-muldiv.h
|
||||||
$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
|
$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
|
||||||
|
80
tests/tcg/x86_64/cross-modifying-code.c
Normal file
80
tests/tcg/x86_64/cross-modifying-code.c
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Test patching code, running in one thread, from another thread.
|
||||||
|
*
|
||||||
|
* Intel SDM calls this "cross-modifying code" and recommends a special
|
||||||
|
* sequence, which requires both threads to cooperate.
|
||||||
|
*
|
||||||
|
* Linux kernel uses a different sequence that does not require cooperation and
|
||||||
|
* involves patching the first byte with int3.
|
||||||
|
*
|
||||||
|
* Finally, there is user-mode software out there that simply uses atomics, and
|
||||||
|
* that seems to be good enough in practice. Test that QEMU has no problems
|
||||||
|
* with this as well.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
void add1_or_nop(long *x);
|
||||||
|
asm(".pushsection .rwx,\"awx\",@progbits\n"
|
||||||
|
".globl add1_or_nop\n"
|
||||||
|
/* addq $0x1,(%rdi) */
|
||||||
|
"add1_or_nop: .byte 0x48, 0x83, 0x07, 0x01\n"
|
||||||
|
"ret\n"
|
||||||
|
".popsection\n");
|
||||||
|
|
||||||
|
#define THREAD_WAIT 0
|
||||||
|
#define THREAD_PATCH 1
|
||||||
|
#define THREAD_STOP 2
|
||||||
|
|
||||||
|
static void *thread_func(void *arg)
|
||||||
|
{
|
||||||
|
int val = 0x0026748d; /* nop */
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
switch (__atomic_load_n((int *)arg, __ATOMIC_SEQ_CST)) {
|
||||||
|
case THREAD_WAIT:
|
||||||
|
break;
|
||||||
|
case THREAD_PATCH:
|
||||||
|
val = __atomic_exchange_n((int *)&add1_or_nop, val,
|
||||||
|
__ATOMIC_SEQ_CST);
|
||||||
|
break;
|
||||||
|
case THREAD_STOP:
|
||||||
|
return NULL;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define INITIAL 42
|
||||||
|
#define COUNT 1000000
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
int command = THREAD_WAIT;
|
||||||
|
pthread_t thread;
|
||||||
|
long x = 0;
|
||||||
|
int err;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
err = pthread_create(&thread, NULL, &thread_func, &command);
|
||||||
|
assert(err == 0);
|
||||||
|
|
||||||
|
__atomic_store_n(&command, THREAD_PATCH, __ATOMIC_SEQ_CST);
|
||||||
|
for (i = 0; i < COUNT; i++) {
|
||||||
|
add1_or_nop(&x);
|
||||||
|
}
|
||||||
|
__atomic_store_n(&command, THREAD_STOP, __ATOMIC_SEQ_CST);
|
||||||
|
|
||||||
|
err = pthread_join(thread, NULL);
|
||||||
|
assert(err == 0);
|
||||||
|
|
||||||
|
assert(x >= INITIAL);
|
||||||
|
assert(x <= INITIAL + COUNT);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user