Skip to content

Commit 6b6f714

Browse files
sean-jcbonzini
authored andcommitted
KVM: selftests: Implement memcmp(), memcpy(), and memset() for guest use
Implement memcmp(), memcpy(), and memset() to override the compiler's built-in versions in order to guarantee that the compiler won't generate out-of-line calls to external functions via the PLT. This allows the helpers to be safely used in guest code, as KVM selftests don't support dynamic loading of guest code. Steal the implementations from the kernel's generic versions, sans the optimizations in memcmp() for unaligned accesses. Put the utilities in a separate compilation unit and build with -ffreestanding to fudge around a gcc "feature" where it will optimize memset(), memcpy(), etc... by generating a recursive call. I.e. the compiler optimizes itself into infinite recursion. Alternatively, the individual functions could be tagged with optimize("no-tree-loop-distribute-patterns"), but using "optimize" for anything but debug is discouraged, and Linus NAK'd the use of the flag in the kernel proper[*]. https://lore.kernel.org/lkml/CAHk-=wik-oXnUpfZ6Hw37uLykc-_P0Apyn2XuX-odh-3Nzop8w@mail.gmail.com Cc: Andrew Jones <[email protected]> Cc: Anup Patel <[email protected]> Cc: Atish Patra <[email protected]> Cc: Christian Borntraeger <[email protected]> Cc: Janosch Frank <[email protected]> Cc: Claudio Imbrenda <[email protected]> Signed-off-by: Sean Christopherson <[email protected]> Message-Id: <[email protected]> Reviewed-by: Andrew Jones <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]>
1 parent aae2e72 commit 6b6f714

File tree

2 files changed

+49
-1
lines changed

2 files changed

+49
-1
lines changed

tools/testing/selftests/kvm/Makefile

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ LIBKVM += lib/rbtree.c
4848
LIBKVM += lib/sparsebit.c
4949
LIBKVM += lib/test_util.c
5050

51+
LIBKVM_STRING += lib/string_override.c
52+
5153
LIBKVM_x86_64 += lib/x86_64/apic.c
5254
LIBKVM_x86_64 += lib/x86_64/handlers.S
5355
LIBKVM_x86_64 += lib/x86_64/perf_test_util.c
@@ -220,7 +222,8 @@ LIBKVM_C := $(filter %.c,$(LIBKVM))
220222
LIBKVM_S := $(filter %.S,$(LIBKVM))
221223
LIBKVM_C_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_C))
222224
LIBKVM_S_OBJ := $(patsubst %.S, $(OUTPUT)/%.o, $(LIBKVM_S))
223-
LIBKVM_OBJS = $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ)
225+
LIBKVM_STRING_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_STRING))
226+
LIBKVM_OBJS = $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ) $(LIBKVM_STRING_OBJ)
224227

225228
EXTRA_CLEAN += $(LIBKVM_OBJS) cscope.*
226229

@@ -231,6 +234,12 @@ $(LIBKVM_C_OBJ): $(OUTPUT)/%.o: %.c
231234
$(LIBKVM_S_OBJ): $(OUTPUT)/%.o: %.S
232235
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
233236

237+
# Compile the string overrides as freestanding to prevent the compiler from
238+
# generating self-referential code, e.g. without "freestanding" the compiler may
239+
# "optimize" memcmp() by invoking memcmp(), thus causing infinite recursion.
240+
$(LIBKVM_STRING_OBJ): $(OUTPUT)/%.o: %.c
241+
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -ffreestanding $< -o $@
242+
234243
x := $(shell mkdir -p $(sort $(dir $(TEST_GEN_PROGS))))
235244
$(TEST_GEN_PROGS): $(LIBKVM_OBJS)
236245
$(TEST_GEN_PROGS_EXTENDED): $(LIBKVM_OBJS)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
#include <stddef.h>
3+
4+
/*
5+
* Override the "basic" built-in string helpers so that they can be used in
6+
* guest code. KVM selftests don't support dynamic loading in guest code and
7+
* will jump into the weeds if the compiler decides to insert an out-of-line
8+
* call via the PLT.
9+
*/
10+
int memcmp(const void *cs, const void *ct, size_t count)
11+
{
12+
const unsigned char *su1, *su2;
13+
int res = 0;
14+
15+
for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--) {
16+
if ((res = *su1 - *su2) != 0)
17+
break;
18+
}
19+
return res;
20+
}
21+
22+
void *memcpy(void *dest, const void *src, size_t count)
23+
{
24+
char *tmp = dest;
25+
const char *s = src;
26+
27+
while (count--)
28+
*tmp++ = *s++;
29+
return dest;
30+
}
31+
32+
void *memset(void *s, int c, size_t count)
33+
{
34+
char *xs = s;
35+
36+
while (count--)
37+
*xs++ = c;
38+
return s;
39+
}

0 commit comments

Comments
 (0)