Commit 53b57054 authored by Mathieu Desnoyers's avatar Mathieu Desnoyers Committed by Wen Zhiwei
Browse files

selftests/rseq: Fix mm_cid test failure

stable inclusion
from stable-v6.6.57
commit 73a4f5a704a2022484e9c42ea06ef05ac42a8621
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/IB2M97

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=73a4f5a704a2022484e9c42ea06ef05ac42a8621



--------------------------------

commit a0cc649353bb726d4aa0db60dce467432197b746 upstream.

Adapt the rseq.c/rseq.h code to follow GNU C library changes introduced by:

glibc commit 2e456ccf0c34 ("Linux: Make __rseq_size useful for feature detection (bug 31965)")

Without this fix, rseq selftests for mm_cid fail:

./run_param_test.sh
Default parameters
Running test spinlock
Running compare-twice test spinlock
Running mm_cid test spinlock
Error: cpu id getter unavailable

Fixes: 18c23558 ("selftests/rseq: Implement rseq mm_cid field support")
Signed-off-by: default avatarMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Peter Zijlstra <peterz@infradead.org>
CC: Boqun Feng <boqun.feng@gmail.com>
CC: "Paul E. McKenney" <paulmck@kernel.org>
Cc: Shuah Khan <skhan@linuxfoundation.org>
CC: Carlos O'Donell <carlos@redhat.com>
CC: Florian Weimer <fweimer@redhat.com>
CC: linux-kselftest@vger.kernel.org
CC: stable@vger.kernel.org
Signed-off-by: default avatarShuah Khan <skhan@linuxfoundation.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarWen Zhiwei <wenzhiwei@kylinos.cn>
parent f1e5a76e
Loading
Loading
Loading
Loading
+75 −35
Original line number Diff line number Diff line
@@ -60,12 +60,6 @@ unsigned int rseq_size = -1U;
/* Flags used during rseq registration.  */
unsigned int rseq_flags;

/*
 * rseq feature size supported by the kernel. 0 if the registration was
 * unsuccessful.
 */
unsigned int rseq_feature_size = -1U;

static int rseq_ownership;
static int rseq_reg_success;	/* At least one rseq registration has succeded. */

@@ -111,6 +105,43 @@ int rseq_available(void)
	}
}

/* The rseq areas need to be at least 32 bytes. */
static
unsigned int get_rseq_min_alloc_size(void)
{
	unsigned int alloc_size = rseq_size;

	if (alloc_size < ORIG_RSEQ_ALLOC_SIZE)
		alloc_size = ORIG_RSEQ_ALLOC_SIZE;
	return alloc_size;
}

/*
 * Return the feature size supported by the kernel.
 *
 * Depending on the value returned by getauxval(AT_RSEQ_FEATURE_SIZE):
 *
 * 0:   Return ORIG_RSEQ_FEATURE_SIZE (20)
 * > 0: Return the value from getauxval(AT_RSEQ_FEATURE_SIZE).
 *
 * It should never return a value below ORIG_RSEQ_FEATURE_SIZE.
 */
static
unsigned int get_rseq_kernel_feature_size(void)
{
	unsigned long auxv_rseq_feature_size, auxv_rseq_align;

	auxv_rseq_align = getauxval(AT_RSEQ_ALIGN);
	assert(!auxv_rseq_align || auxv_rseq_align <= RSEQ_THREAD_AREA_ALLOC_SIZE);

	auxv_rseq_feature_size = getauxval(AT_RSEQ_FEATURE_SIZE);
	assert(!auxv_rseq_feature_size || auxv_rseq_feature_size <= RSEQ_THREAD_AREA_ALLOC_SIZE);
	if (auxv_rseq_feature_size)
		return auxv_rseq_feature_size;
	else
		return ORIG_RSEQ_FEATURE_SIZE;
}

int rseq_register_current_thread(void)
{
	int rc;
@@ -119,7 +150,7 @@ int rseq_register_current_thread(void)
		/* Treat libc's ownership as a successful registration. */
		return 0;
	}
	rc = sys_rseq(&__rseq_abi, rseq_size, 0, RSEQ_SIG);
	rc = sys_rseq(&__rseq_abi, get_rseq_min_alloc_size(), 0, RSEQ_SIG);
	if (rc) {
		if (RSEQ_READ_ONCE(rseq_reg_success)) {
			/* Incoherent success/failure within process. */
@@ -140,28 +171,12 @@ int rseq_unregister_current_thread(void)
		/* Treat libc's ownership as a successful unregistration. */
		return 0;
	}
	rc = sys_rseq(&__rseq_abi, rseq_size, RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG);
	rc = sys_rseq(&__rseq_abi, get_rseq_min_alloc_size(), RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG);
	if (rc)
		return -1;
	return 0;
}

static
unsigned int get_rseq_feature_size(void)
{
	unsigned long auxv_rseq_feature_size, auxv_rseq_align;

	auxv_rseq_align = getauxval(AT_RSEQ_ALIGN);
	assert(!auxv_rseq_align || auxv_rseq_align <= RSEQ_THREAD_AREA_ALLOC_SIZE);

	auxv_rseq_feature_size = getauxval(AT_RSEQ_FEATURE_SIZE);
	assert(!auxv_rseq_feature_size || auxv_rseq_feature_size <= RSEQ_THREAD_AREA_ALLOC_SIZE);
	if (auxv_rseq_feature_size)
		return auxv_rseq_feature_size;
	else
		return ORIG_RSEQ_FEATURE_SIZE;
}

static __attribute__((constructor))
void rseq_init(void)
{
@@ -178,28 +193,54 @@ void rseq_init(void)
	}
	if (libc_rseq_size_p && libc_rseq_offset_p && libc_rseq_flags_p &&
			*libc_rseq_size_p != 0) {
		unsigned int libc_rseq_size;

		/* rseq registration owned by glibc */
		rseq_offset = *libc_rseq_offset_p;
		rseq_size = *libc_rseq_size_p;
		libc_rseq_size = *libc_rseq_size_p;
		rseq_flags = *libc_rseq_flags_p;
		rseq_feature_size = get_rseq_feature_size();
		if (rseq_feature_size > rseq_size)
			rseq_feature_size = rseq_size;

		/*
		 * Previous versions of glibc expose the value
		 * 32 even though the kernel only supported 20
		 * bytes initially. Therefore treat 32 as a
		 * special-case. glibc 2.40 exposes a 20 bytes
		 * __rseq_size without using getauxval(3) to
		 * query the supported size, while still allocating a 32
		 * bytes area. Also treat 20 as a special-case.
		 *
		 * Special-cases are handled by using the following
		 * value as active feature set size:
		 *
		 *   rseq_size = min(32, get_rseq_kernel_feature_size())
		 */
		switch (libc_rseq_size) {
		case ORIG_RSEQ_FEATURE_SIZE:
			fallthrough;
		case ORIG_RSEQ_ALLOC_SIZE:
		{
			unsigned int rseq_kernel_feature_size = get_rseq_kernel_feature_size();

			if (rseq_kernel_feature_size < ORIG_RSEQ_ALLOC_SIZE)
				rseq_size = rseq_kernel_feature_size;
			else
				rseq_size = ORIG_RSEQ_ALLOC_SIZE;
			break;
		}
		default:
			/* Otherwise just use the __rseq_size from libc as rseq_size. */
			rseq_size = libc_rseq_size;
			break;
		}
		return;
	}
	rseq_ownership = 1;
	if (!rseq_available()) {
		rseq_size = 0;
		rseq_feature_size = 0;
		return;
	}
	rseq_offset = (void *)&__rseq_abi - rseq_thread_pointer();
	rseq_flags = 0;
	rseq_feature_size = get_rseq_feature_size();
	if (rseq_feature_size == ORIG_RSEQ_FEATURE_SIZE)
		rseq_size = ORIG_RSEQ_ALLOC_SIZE;
	else
		rseq_size = RSEQ_THREAD_AREA_ALLOC_SIZE;
}

static __attribute__((destructor))
@@ -209,7 +250,6 @@ void rseq_exit(void)
		return;
	rseq_offset = 0;
	rseq_size = -1U;
	rseq_feature_size = -1U;
	rseq_ownership = 0;
}

+2 −8
Original line number Diff line number Diff line
@@ -68,12 +68,6 @@ extern unsigned int rseq_size;
/* Flags used during rseq registration. */
extern unsigned int rseq_flags;

/*
 * rseq feature size supported by the kernel. 0 if the registration was
 * unsuccessful.
 */
extern unsigned int rseq_feature_size;

enum rseq_mo {
	RSEQ_MO_RELAXED = 0,
	RSEQ_MO_CONSUME = 1,	/* Unused */
@@ -193,7 +187,7 @@ static inline uint32_t rseq_current_cpu(void)

static inline bool rseq_node_id_available(void)
{
	return (int) rseq_feature_size >= rseq_offsetofend(struct rseq_abi, node_id);
	return (int) rseq_size >= rseq_offsetofend(struct rseq_abi, node_id);
}

/*
@@ -207,7 +201,7 @@ static inline uint32_t rseq_current_node_id(void)

static inline bool rseq_mm_cid_available(void)
{
	return (int) rseq_feature_size >= rseq_offsetofend(struct rseq_abi, mm_cid);
	return (int) rseq_size >= rseq_offsetofend(struct rseq_abi, mm_cid);
}

static inline uint32_t rseq_current_mm_cid(void)