[LTP] [PATCH 12/29] Hugetlb: Migrating libhugetlbfs icache-hygiene

Cyril Hrubis chrubis@suse.cz
Mon Oct 17 21:37:30 CEST 2022


Hi!
> Migrating the libhugetlbfs/testcases/icache-hygiene.c test
> 
> Test Description: Older ppc64 kernels don't properly flush dcache to
> icache before giving a cleared page to userspace.  With some exceedingly
> hairy code, this attempts to test for this bug.
> 
> This test will never trigger (obviously) on machines with coherent
> icache and dcache (including x86 and POWER5).  On any given run,
> even on a buggy kernel there's a chance the bug won't trigger -
> either because we don't get the same physical page back when we
> remap, or because the icache happens to get flushed in the interim.
> 
> Signed-off-by: Tarun Sahu <tsahu@linux.ibm.com>
> ---
>  runtest/hugetlb                               |   1 +
>  testcases/kernel/mem/.gitignore               |   1 +
>  .../kernel/mem/hugetlb/hugemmap/hugemmap15.c  | 250 ++++++++++++++++++
>  3 files changed, 252 insertions(+)
>  create mode 100644 testcases/kernel/mem/hugetlb/hugemmap/hugemmap15.c
> 
> diff --git a/runtest/hugetlb b/runtest/hugetlb
> index 796ebe7fa..0714ed34c 100644
> --- a/runtest/hugetlb
> +++ b/runtest/hugetlb
> @@ -16,6 +16,7 @@ hugemmap11 hugemmap11
>  hugemmap12 hugemmap12
>  hugemmap13 hugemmap13
>  hugemmap14 hugemmap14
> +hugemmap15 hugemmap15
>  hugemmap05_1 hugemmap05 -m
>  hugemmap05_2 hugemmap05 -s
>  hugemmap05_3 hugemmap05 -s -m
> diff --git a/testcases/kernel/mem/.gitignore b/testcases/kernel/mem/.gitignore
> index 3106579ce..d59b60fd4 100644
> --- a/testcases/kernel/mem/.gitignore
> +++ b/testcases/kernel/mem/.gitignore
> @@ -15,6 +15,7 @@
>  /hugetlb/hugemmap/hugemmap12
>  /hugetlb/hugemmap/hugemmap13
>  /hugetlb/hugemmap/hugemmap14
> +/hugetlb/hugemmap/hugemmap15
>  /hugetlb/hugeshmat/hugeshmat01
>  /hugetlb/hugeshmat/hugeshmat02
>  /hugetlb/hugeshmat/hugeshmat03
> diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap15.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap15.c
> new file mode 100644
> index 000000000..cf1cd96ea
> --- /dev/null
> +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap15.c
> @@ -0,0 +1,250 @@
> +// SPDX-License-Identifier: LGPL-2.1-or-later
> +/*
> + * Copyright (C) 2005-2006 David Gibson & Adam Litke, IBM Corporation.
> + *
> + * Test Name: icache hygiene
> + *
> + * Test Description: Older ppc64 kernels don't properly flush dcache to
> + * icache before giving a cleared page to userspace.  With some exceedingly
> + * hairy code, this attempts to test for this bug.
> + *
> + * This test will never trigger (obviously) on machines with coherent
> + * icache and dcache (including x86 and POWER5).  On any given run,
> + * even on a buggy kernel there's a chance the bug won't trigger -
> + * either because we don't get the same physical page back when we
> + * remap, or because the icache happens to get flushed in the interim.
> + *
> + * HISTORY
> + *  Written by David Gibson & Adam Litke
> + *
> + */
> +
> +#define _GNU_SOURCE
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <setjmp.h>
> +#include <unistd.h>
> +#include <signal.h>
> +#include <sys/mman.h>
> +#include <ucontext.h>
> +#include <sys/mount.h>
> +#include <limits.h>
> +#include <sys/param.h>
> +#include <sys/types.h>
> +
> +#include "hugetlb.h"
> +
> +static int  fd = -1;
> +static char hfile[MAXPATHLEN];
> +
> +#define COPY_SIZE	128
> +#define NUM_REPETITIONS	64	/* Seems to be enough to trigger reliably */
> +
> +static char *verbose;
> +static long hpage_size;
> +
> +static void cacheflush(void *p)
> +{
> +	(void)p;
> +#if defined(__powerpc__)
> +	asm volatile("dcbst 0,%0; sync; icbi 0,%0; isync" : : "r"(p));
> +#elif defined(__arm__) || defined(__aarch64__)
> +	__clear_cache(p, p + COPY_SIZE);
> +#endif
> +}
> +
> +static void jumpfunc(int copy, void *p)
> +{
> +	/* gcc bug workaround: if there is exactly one &&label
> +	 * construct in the function, gcc assumes the computed goto
> +	 * goes there, leading to the complete elision of the goto in
> +	 * this case
> +	 */
> +	void *l = &&dummy;
> +
> +	l = &&jumplabel;
> +
> +	if (copy) {
> +		memcpy(p, l, COPY_SIZE);
> +		cacheflush(p);
> +	}
> +
> +	goto *p;
> + dummy:
> +	tst_res(TWARN, "unreachable?");
> +
> + jumplabel:
> +	return;
> +}
> +
> +static sigjmp_buf sig_escape;
> +static void *sig_expected;
> +
> +static void sig_handler(int signum, siginfo_t *si, void *uc)
> +{
> +#if defined(__powerpc__) || defined(__powerpc64__) || defined(__ia64__) || \
> +	defined(__s390__) || defined(__s390x__) || defined(__sparc__) || \
> +	defined(__aarch64__) || (defined(__riscv) && __riscv_xlen == 64)
> +	/* On powerpc, ia64, s390 and Aarch64, 0 bytes are an illegal
> +	 * instruction, so, if the icache is cleared properly, we SIGILL
> +	 * as soon as we jump into the cleared page
> +	 */
> +	if (signum == SIGILL) {
> +		if (verbose)
> +			tst_res(TINFO, "SIGILL at %p (sig_expected=%p)", si->si_addr,
> +				       sig_expected);
> +		if (si->si_addr == sig_expected)
> +			siglongjmp(sig_escape, 1);
> +		tst_res(TFAIL, "SIGILL somewhere unexpected");
> +		goto fail;
> +	}
> +#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__)
> +	/* On x86, zero bytes form a valid instruction:
> +	 *	add %al,(%eax)		(i386)
> +	 * or	add %al,(%rax)		(x86_64)
> +	 *
> +	 * So, behaviour depends on the contents of [ER]AX, which in
> +	 * turn depends on the details of code generation.  If [ER]AX
> +	 * contains a valid pointer, we will execute the instruction
> +	 * repeatedly until we run off that hugepage and get a SIGBUS
> +	 * on the second, truncated page.  If [ER]AX does not contain
> +	 * a valid pointer, we will SEGV on the first instruction in
> +	 * the cleared page.  We check for both possibilities
> +	 * below.
> +	 *
> +	 * On 32 bit ARM, zero bytes are interpreted as follows:
> +	 *  andeq	r0, r0, r0	(ARM state, 4 bytes)
> +	 *  movs	r0, r0		(Thumb state, 2 bytes)
> +	 *
> +	 * So, we only expect to run off the end of the huge page and
> +	 * generate a SIGBUS.
> +	 */
> +	if (signum == SIGBUS) {
> +		if (verbose)
> +			tst_res(TINFO, "SIGBUS at %p (sig_expected=%p)", si->si_addr,
> +				       sig_expected);
> +		if (sig_expected
> +		    && (ALIGN((unsigned long)sig_expected, hpage_size)
> +			== (unsigned long)si->si_addr)) {
> +			siglongjmp(sig_escape, 2);
> +		}
> +		tst_res(TFAIL, "SIGBUS somewhere unexpected");
> +		goto fail;
> +	}
> +#if defined(__x86_64__) || defined(__i386__)
> +	if (signum == SIGSEGV) {
> +#ifdef __x86_64__
> +		void *pc = (void *)((ucontext_t *)uc)->uc_mcontext.gregs[REG_RIP];
> +#else
> +		void *pc = (void *)((ucontext_t *)uc)->uc_mcontext.gregs[REG_EIP];
> +#endif
> +		if (verbose)
> +			tst_res(TINFO, "SIGSEGV at %p, PC=%p (sig_expected=%p)\n",
> +				       si->si_addr, pc, sig_expected);
> +		if (sig_expected == pc)
> +			siglongjmp(sig_escape, 1);
> +		tst_res(TFAIL, "SIGSEGV somewhere unexpected");
> +		goto fail;
> +	}
> +#endif
> +#else
> +#error "Need to setup signal conditions for this arch"
> +#endif
> +	return;
> +fail:
> +	tst_brk(TBROK, "Once Failed, No Need to continue the next iteration.");
> +}

Again no tst_res() or tst_brk() is allowed inside a signal handler, the
best we can is to propagate different values for pass/fail in the return
value from siglongjmp().


And the usuall comments apply to this test as well.

-- 
Cyril Hrubis
chrubis@suse.cz


More information about the ltp mailing list