All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Iván Ezequiel Rodriguez" <ivanrwcm25@gmail.com>
To: Catalin Marinas <catalin.marinas@arm.com>, Will Deacon <will@kernel.org>
Cc: Mark Brown <broonie@kernel.org>, Shuah Khan <shuah@kernel.org>,
	Mark Rutland <mark.rutland@arm.com>,
	linux-arm-kernel@lists.infradead.org,
	linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH 1/1] selftests/arm64: GCS must survive io_uring SQPOLL worker exit
Date: Fri, 30 Oct 2026 14:00:46 -0300	[thread overview]
Message-ID: <20261030170046.439736-2-ivanrwcm25@gmail.com> (raw)
In-Reply-To: <20261030170046.439736-1-ivanrwcm25@gmail.com>

SQPOLL threads are create_io_thread() workers (args->fn + CLONE_VM).
They inherit GCS tracking via arch_dup_task_struct(); without
gcs_reset_thread() their exit runs gcs_free() on the parent's stack.

Create a SQPOLL ring, wait for iou-sqp-* to appear and leave
/proc/self/task (close only queues async teardown), then check the
parent ss VMA count is unchanged. QEMU: pass with the reset, SIGSEGV
without it.

Signed-off-by: Iván Ezequiel Rodriguez <ivanrwcm25@gmail.com>
---
 tools/testing/selftests/arm64/gcs/Makefile    |   2 +-
 .../selftests/arm64/gcs/gcs-uring-worker.c    | 254 ++++++++++++++++++
 2 files changed, 255 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/arm64/gcs/gcs-uring-worker.c

diff --git a/tools/testing/selftests/arm64/gcs/Makefile b/tools/testing/selftests/arm64/gcs/Makefile
index f1365eea31c9..a427041f8cd1 100644
--- a/tools/testing/selftests/arm64/gcs/Makefile
+++ b/tools/testing/selftests/arm64/gcs/Makefile
@@ -7,7 +7,7 @@
 #
 
 TEST_GEN_PROGS := basic-gcs libc-gcs gcs-locking gcs-stress gcspushm gcsstr \
-		  gcs-clone-pidfd
+		  gcs-clone-pidfd gcs-uring-worker
 TEST_GEN_PROGS_EXTENDED := gcs-stress-thread
 
 LDLIBS+=-lpthread
diff --git a/tools/testing/selftests/arm64/gcs/gcs-uring-worker.c b/tools/testing/selftests/arm64/gcs/gcs-uring-worker.c
new file mode 100644
index 000000000000..737e1a14177b
--- /dev/null
+++ b/tools/testing/selftests/arm64/gcs/gcs-uring-worker.c
@@ -0,0 +1,254 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * io_uring SQPOLL uses create_io_thread() (args->fn + CLONE_VM) and
+ * inherits GCS tracking via arch_dup_task_struct(). Without
+ * gcs_reset_thread() on that path, worker exit runs deactivate_mm()
+ * -> gcs_free() and unmaps the parent's GCS.
+ *
+ * close(2) on the ring only queues teardown; wait for the iou-sqp-*
+ * thread to leave /proc/self/task so gcs_free() has finished before
+ * counting VMAs.
+ *
+ * After enabling GCS, do not return from main() through libc frames
+ * that were pushed before enable.
+ */
+#define _GNU_SOURCE
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/io_uring.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/auxv.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <asm/hwcap.h>
+
+#include "kselftest.h"
+#include "gcs-util.h"
+
+#ifndef HWCAP_GCS
+#define HWCAP_GCS			(1UL << 32)
+#endif
+
+#ifndef __NR_io_uring_setup
+#define __NR_io_uring_setup		425
+#endif
+#ifndef __NR_io_uring_enter
+#define __NR_io_uring_enter		426
+#endif
+
+#define SQPOLL_WAIT_MS			5000
+
+struct uring {
+	int fd;
+	struct io_uring_params params;
+	void *sq;
+	size_t sq_len;
+	struct io_uring_sqe *sqes;
+	size_t sqes_len;
+};
+
+static int count_ss_vmas(void)
+{
+	FILE *f;
+	char line[256];
+	int vmas = 0;
+
+	f = fopen("/proc/self/smaps", "r");
+	if (!f)
+		ksft_exit_fail_msg("fopen /proc/self/smaps: %s\n",
+				   strerror(errno));
+
+	while (fgets(line, sizeof(line), f)) {
+		if (!strncmp(line, "VmFlags:", 8) && strstr(line, " ss"))
+			vmas++;
+	}
+	fclose(f);
+	return vmas;
+}
+
+static bool has_iou_sqp(void)
+{
+	DIR *d;
+	struct dirent *de;
+	char path[96], comm[32];
+	int fd, n;
+	bool found = false;
+
+	d = opendir("/proc/self/task");
+	if (!d)
+		return false;
+
+	while ((de = readdir(d))) {
+		if (de->d_name[0] < '1' || de->d_name[0] > '9')
+			continue;
+		if (snprintf(path, sizeof(path), "/proc/self/task/%s/comm",
+			     de->d_name) >= (int)sizeof(path))
+			continue;
+		fd = open(path, O_RDONLY);
+		if (fd < 0)
+			continue;
+		n = read(fd, comm, sizeof(comm) - 1);
+		close(fd);
+		if (n <= 0)
+			continue;
+		comm[n] = '\0';
+		if (!strncmp(comm, "iou-sqp-", 8)) {
+			found = true;
+			break;
+		}
+	}
+	closedir(d);
+	return found;
+}
+
+static int wait_iou_sqp(bool want, int timeout_ms)
+{
+	int slept = 0;
+
+	while (has_iou_sqp() != want) {
+		if (slept >= timeout_ms)
+			return -ETIMEDOUT;
+		usleep(1000);
+		slept++;
+	}
+	return 0;
+}
+
+static void uring_close(struct uring *r)
+{
+	if (r->sqes && r->sqes != MAP_FAILED) {
+		munmap(r->sqes, r->sqes_len);
+		r->sqes = MAP_FAILED;
+	}
+	if (r->sq && r->sq != MAP_FAILED) {
+		munmap(r->sq, r->sq_len);
+		r->sq = MAP_FAILED;
+	}
+	if (r->fd >= 0) {
+		close(r->fd);
+		r->fd = -1;
+	}
+}
+
+static int uring_sqpoll_open(struct uring *r)
+{
+	size_t cq_len;
+	int ret;
+
+	memset(r, 0, sizeof(*r));
+	r->fd = -1;
+	r->sq = MAP_FAILED;
+	r->sqes = MAP_FAILED;
+
+	r->params.flags = IORING_SETUP_SQPOLL;
+	r->params.sq_thread_idle = 100;
+	r->fd = syscall(__NR_io_uring_setup, 8, &r->params);
+	if (r->fd < 0)
+		return -errno;
+
+	if (!(r->params.features & IORING_FEAT_SINGLE_MMAP)) {
+		ret = -EOPNOTSUPP;
+		goto err;
+	}
+
+	r->sq_len = r->params.sq_off.array +
+		    r->params.sq_entries * sizeof(unsigned int);
+	cq_len = r->params.cq_off.cqes +
+		 r->params.cq_entries * sizeof(struct io_uring_cqe);
+	if (cq_len > r->sq_len)
+		r->sq_len = cq_len;
+
+	r->sq = mmap(NULL, r->sq_len, PROT_READ | PROT_WRITE,
+		     MAP_SHARED | MAP_POPULATE, r->fd, IORING_OFF_SQ_RING);
+	if (r->sq == MAP_FAILED) {
+		ret = -errno;
+		goto err;
+	}
+
+	r->sqes_len = r->params.sq_entries * sizeof(struct io_uring_sqe);
+	r->sqes = mmap(NULL, r->sqes_len, PROT_READ | PROT_WRITE,
+		       MAP_SHARED | MAP_POPULATE, r->fd, IORING_OFF_SQES);
+	if (r->sqes == MAP_FAILED) {
+		ret = -errno;
+		goto err;
+	}
+
+	return 0;
+err:
+	uring_close(r);
+	return ret;
+}
+
+int main(void)
+{
+	struct uring ring;
+	unsigned long gcs_mode;
+	long rc;
+	int vmas0, vmas1;
+
+	ksft_print_header();
+
+	if (!(getauxval(AT_HWCAP) & HWCAP_GCS))
+		ksft_exit_skip("GCS not supported\n");
+
+	rc = my_syscall2(__NR_prctl, PR_GET_SHADOW_STACK_STATUS, &gcs_mode);
+	if (rc)
+		ksft_exit_fail_msg("PR_GET_SHADOW_STACK_STATUS: %ld\n", rc);
+
+	if (!(gcs_mode & PR_SHADOW_STACK_ENABLE)) {
+		rc = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
+				 PR_SHADOW_STACK_ENABLE);
+		if (rc == -EOPNOTSUPP || rc == -EINVAL || rc == -EBUSY)
+			ksft_exit_skip("cannot enable GCS: %ld\n", rc);
+		if (rc)
+			ksft_exit_fail_msg("PR_SET_SHADOW_STACK_STATUS: %ld\n",
+					   rc);
+	}
+
+	rc = uring_sqpoll_open(&ring);
+	if (rc == -ENOSYS || rc == -EINVAL || rc == -EPERM ||
+	    rc == -EOPNOTSUPP || rc == -EACCES)
+		ksft_exit_skip("io_uring SQPOLL unavailable: %s\n",
+			       strerror(-rc));
+	if (rc)
+		ksft_exit_fail_msg("io_uring_setup SQPOLL: %s\n",
+				   strerror(-rc));
+
+	rc = syscall(__NR_io_uring_enter, ring.fd, 0, 0,
+		     IORING_ENTER_SQ_WAKEUP, NULL, 0);
+	if (rc < 0)
+		ksft_exit_fail_msg("io_uring_enter SQ_WAKEUP: %s\n",
+				   strerror(errno));
+
+	if (wait_iou_sqp(true, SQPOLL_WAIT_MS)) {
+		uring_close(&ring);
+		ksft_exit_fail_msg("SQPOLL thread did not appear\n");
+	}
+
+	vmas0 = count_ss_vmas();
+	if (vmas0 < 1) {
+		uring_close(&ring);
+		ksft_exit_fail_msg("expected a GCS VMA before worker exit\n");
+	}
+
+	ksft_set_plan(1);
+
+	uring_close(&ring);
+
+	if (wait_iou_sqp(false, SQPOLL_WAIT_MS))
+		ksft_exit_fail_msg("SQPOLL thread did not exit\n");
+
+	vmas1 = count_ss_vmas();
+	ksft_test_result(vmas1 == vmas0,
+			 "parent GCS VMA survives SQPOLL exit (%d -> %d)\n",
+			 vmas0, vmas1);
+
+	ksft_finished();
+}
-- 
2.43.0


      reply	other threads:[~2026-10-30 17:01 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-28 15:43 [PATCH v3 0/4] arm64, riscv: release shadow stacks on failed fork Iván Ezequiel Rodriguez
2026-10-28 15:43 ` Iván Ezequiel Rodriguez
2026-10-28 15:43 ` [PATCH v3 1/4] arm64/gcs: release GCS " Iván Ezequiel Rodriguez
2026-10-28 15:43   ` Iván Ezequiel Rodriguez
2026-10-28 18:41   ` Mark Brown
2026-10-28 18:41     ` Mark Brown
2026-10-29 20:32   ` Catalin Marinas
2026-10-29 20:32     ` Catalin Marinas
2026-10-29 22:36     ` Mark Brown
2026-10-29 22:36       ` Mark Brown
2026-10-28 15:43 ` [PATCH v3 2/4] riscv/shstk: release shadow stack " Iván Ezequiel Rodriguez
2026-10-28 15:43   ` Iván Ezequiel Rodriguez
2026-10-28 15:43 ` [PATCH v3 3/4] selftests/arm64: GCS must not leak on a failed clone3 Iván Ezequiel Rodriguez
2026-10-28 15:43   ` Iván Ezequiel Rodriguez
2026-10-28 18:31   ` Mark Brown
2026-10-28 18:31     ` Mark Brown
2026-10-28 15:43 ` [PATCH v3 4/4] selftests/riscv: shadow stack " Iván Ezequiel Rodriguez
2026-10-28 15:43   ` Iván Ezequiel Rodriguez
2026-10-29 20:57 ` (subset) [PATCH v3 0/4] arm64, riscv: release shadow stacks on failed fork Catalin Marinas
2026-10-29 20:57   ` Catalin Marinas
2026-10-30 17:00   ` [PATCH 0/1] selftests/arm64: GCS must survive io_uring SQPOLL worker exit Iván Ezequiel Rodriguez
2026-10-30 17:00     ` Iván Ezequiel Rodriguez [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261030170046.439736-2-ivanrwcm25@gmail.com \
    --to=ivanrwcm25@gmail.com \
    --cc=broonie@kernel.org \
    --cc=catalin.marinas@arm.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=shuah@kernel.org \
    --cc=will@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.