An AMX task gets a dynamically allocated fpstate after requesting XTILEDATA permission and first loading tile data. Test that exec() releases this allocation instead of making it unreachable.
Run ten request, XRSTOR and self-exec cycles in the same task. Count the matching allocations in /proc/vmallocinfo before the test, while the first allocation is live and after the task exits. Skip the test when /proc/vmallocinfo is unavailable or when concurrent global vmalloc changes prevent an isolated measurement. The test observed 10 leaked allocations on an unfixed kernel and zero with the fix applied. Signed-off-by: Guixiong Wei <[email protected]> --- tools/testing/selftests/x86/amx.c | 144 +++++++++++++++++++++++++++++- 1 file changed, 143 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/x86/amx.c b/tools/testing/selftests/x86/amx.c index 40769c16de1bb..cf098dfbe50c0 100644 --- a/tools/testing/selftests/x86/amx.c +++ b/tools/testing/selftests/x86/amx.c @@ -3,8 +3,10 @@ #define _GNU_SOURCE #include <err.h> #include <errno.h> +#include <limits.h> #include <setjmp.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <stdbool.h> #include <unistd.h> @@ -30,6 +32,9 @@ #define XFEATURE_MASK_XTILEDATA (1 << XFEATURE_XTILEDATA) #define XFEATURE_MASK_XTILE (XFEATURE_MASK_XTILECFG | XFEATURE_MASK_XTILEDATA) +#define EXEC_TEST_ARG "--exec-test" +#define EXEC_TEST_ITERS 10 + struct xstate_info xtiledata; /* The helpers for managing XSAVE buffer and tile states: */ @@ -478,11 +483,144 @@ static void test_fork(void) _exit(0); } -int main(void) +static int count_dynamic_fpstates(void) +{ + char *line = NULL; + size_t line_size = 0; + FILE *fp; + int count = 0; + + fp = fopen("/proc/vmallocinfo", "r"); + if (!fp) + return -errno; + + while (getline(&line, &line_size, fp) >= 0) { + if (strstr(line, "__xfd_enable_feature") || + strstr(line, "fpstate_realloc")) + count++; + } + + if (ferror(fp)) + count = -EIO; + + free(line); + fclose(fp); + return count; +} + +static int parse_exec_arg(const char *arg) +{ + char *end; + long value; + + errno = 0; + value = strtol(arg, &end, 10); + if (errno || *end || value < 0 || value > INT_MAX) + fatal_error("invalid exec test argument: %s", arg); + + return value; +} + +static int run_exec_test(int iterations, int baseline) +{ + char iterations_arg[16]; + char baseline_arg[16]; + int allocated; + + if (!iterations) + return 0; + + req_xtiledata_perm(); + if (!load_rand_tiledata(stashed_xsave)) + fatal_error("failed to load tiledata before exec()"); + + if (iterations == EXEC_TEST_ITERS) { + allocated = count_dynamic_fpstates(); + if (allocated != baseline + 1) + return KSFT_SKIP; + } + + snprintf(iterations_arg, sizeof(iterations_arg), "%d", iterations - 1); + snprintf(baseline_arg, sizeof(baseline_arg), "%d", baseline); + execl("/proc/self/exe", "amx", EXEC_TEST_ARG, iterations_arg, + baseline_arg, NULL); + fatal_error("exec"); +} + +static void test_exec(void) +{ + char iterations_arg[16]; + char baseline_arg[16]; + int before, after, delta; + pid_t child; + int status; + + printf("[RUN]\tCheck dynamic fpstate cleanup across exec().\n"); + + before = count_dynamic_fpstates(); + if (before < 0) { + printf("[SKIP]\tCannot read /proc/vmallocinfo: %s\n", + strerror(-before)); + return; + } + + child = fork(); + if (child < 0) + fatal_error("fork"); + if (!child) { + snprintf(iterations_arg, sizeof(iterations_arg), "%d", + EXEC_TEST_ITERS); + snprintf(baseline_arg, sizeof(baseline_arg), "%d", before); + execl("/proc/self/exe", "amx", EXEC_TEST_ARG, iterations_arg, + baseline_arg, NULL); + fatal_error("exec"); + } + + if (waitpid(child, &status, 0) != child) + fatal_error("waitpid"); + if (WIFEXITED(status) && WEXITSTATUS(status) == KSFT_SKIP) { + printf("[SKIP]\tDynamic fpstate allocation was not isolated.\n"); + return; + } + if (!WIFEXITED(status) || WEXITSTATUS(status)) + fatal_error("exec test child"); + + after = count_dynamic_fpstates(); + if (after < 0) { + printf("[SKIP]\tCannot read /proc/vmallocinfo after exec(): %s\n", + strerror(-after)); + return; + } + + delta = after - before; + if (!delta) { + printf("[OK]\tDynamic fpstate allocations were freed across exec().\n"); + return; + } + + if (delta == EXEC_TEST_ITERS) + errx(1, "[FAIL]\texec() leaked %d dynamic fpstate allocations", + delta); + + printf("[SKIP]\tDynamic fpstate vmalloc usage changed concurrently.\n"); +} + +int main(int argc, char **argv) { unsigned long features; + int iterations = 0; + int baseline = 0; + bool exec_test; long rc; + exec_test = argc == 4 && !strcmp(argv[1], EXEC_TEST_ARG); + if (exec_test) { + iterations = parse_exec_arg(argv[2]); + baseline = parse_exec_arg(argv[3]); + if (!iterations) + return 0; + } + rc = syscall(SYS_arch_prctl, ARCH_GET_XCOMP_SUPP, &features); if (rc || (features & XFEATURE_MASK_XTILE) != XFEATURE_MASK_XTILE) { ksft_print_msg("no AMX support\n"); @@ -498,6 +636,10 @@ int main(void) init_stashed_xsave(); sethandler(SIGILL, handle_noperm, 0); + if (exec_test) + return run_exec_test(iterations, baseline); + + test_exec(); test_dynamic_state(); /* Request permission for the following tests */ -- 2.50.1 (Apple Git-155)

