Linux io-uring development
 help / color / mirror / Atom feed
* [PATCH] io_uring: annotate remote tasks for kcoverage
@ 2026-05-20 15:39 Robert Femmer
  2026-05-20 17:36 ` Andrey Konovalov
  0 siblings, 1 reply; 5+ messages in thread
From: Robert Femmer @ 2026-05-20 15:39 UTC (permalink / raw)
  To: io-uring
  Cc: Jens Axboe, Dmitry Vyukov, Andrey Konovalov, kasan-dev,
	Robert Femmer

Fuzzers use coverage information to guide generation of test cases
towards new or interesting code paths. Syzkaller, specifically, makes
use kcoverage (CONFIG_KCOV). Coverage information is not collected for
kernel tasks unless annotated by kcov_remote_start and kcov_remote_stop.
This patch annotates io-uring's work queue and sqpoll tasks.

The value of the handle needs to be passed by userspace when enabling
remote coverage collection. I chose the cgroup ns inum, because it is
predictable and flexible enough for consumers to control which group of
processes should be included for remote coverage collection, should they
create an instance of io-uring.

Signed-off-by: Robert Femmer <robert@fmmr.tech>
---
 include/linux/io_uring_types.h |  4 ++++
 include/uapi/linux/kcov.h      |  1 +
 io_uring/io-wq.c               |  4 ++++
 io_uring/io_uring.c            | 18 ++++++++++++++++++
 io_uring/io_uring.h            | 22 ++++++++++++++++++++++
 io_uring/sqpoll.c              |  4 ++++
 kernel/kcov.c                  |  2 ++
 7 files changed, 55 insertions(+)

diff --git a/include/linux/io_uring_types.h b/include/linux/io_uring_types.h
index 244392026c6d..b92b8e7169ea 100644
--- a/include/linux/io_uring_types.h
+++ b/include/linux/io_uring_types.h
@@ -504,6 +504,10 @@ struct io_ring_ctx {
 	struct io_mapped_region		ring_region;
 	/* used for optimised request parameter and wait argument passing  */
 	struct io_mapped_region		param_region;
+
+#ifdef CONFIG_KCOV
+	u64				kcov_handle;
+#endif
 };
 
 /*
diff --git a/include/uapi/linux/kcov.h b/include/uapi/linux/kcov.h
index ed95dba9fa37..15bbce4569b1 100644
--- a/include/uapi/linux/kcov.h
+++ b/include/uapi/linux/kcov.h
@@ -49,6 +49,7 @@ enum {
 
 #define KCOV_SUBSYSTEM_COMMON	(0x00ull << 56)
 #define KCOV_SUBSYSTEM_USB	(0x01ull << 56)
+#define KCOV_SUBSYSTEM_IOURING	(0x02ull << 56)
 
 #define KCOV_SUBSYSTEM_MASK	(0xffull << 56)
 #define KCOV_INSTANCE_MASK	(0xffffffffull)
diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c
index 8cc7b47d3089..bb89d3f4b3dc 100644
--- a/io_uring/io-wq.c
+++ b/io_uring/io-wq.c
@@ -639,6 +639,7 @@ static void io_worker_handle_work(struct io_wq_acct *acct,
 		/* handle a whole dependent link */
 		do {
 			struct io_wq_work *next_hashed, *linked;
+			struct io_kiocb *req;
 			unsigned int work_flags = atomic_read(&work->flags);
 			unsigned int hash = __io_wq_is_hashed(work_flags)
 				? __io_get_work_hash(work_flags)
@@ -649,7 +650,10 @@ static void io_worker_handle_work(struct io_wq_acct *acct,
 			if (do_kill &&
 			    (work_flags & IO_WQ_WORK_UNBOUND))
 				atomic_or(IO_WQ_WORK_CANCEL, &work->flags);
+			req = container_of(work, struct io_kiocb, work);
+			io_kcov_remote_start(req->ctx);
 			io_wq_submit_work(work);
+			io_kcov_remote_stop();
 			io_assign_current_work(worker, NULL);
 
 			linked = io_wq_free_work(work);
diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c
index 036145ee466c..71478e6ccae4 100644
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -222,6 +222,21 @@ static void io_free_alloc_caches(struct io_ring_ctx *ctx)
 	io_rsrc_cache_free(ctx);
 }
 
+#ifdef CONFIG_KCOV
+static __cold u64 io_ring_get_kcov_handle(void)
+{
+	struct nsproxy *ns_proxy = current->nsproxy;
+	struct ns_common *ns;
+	u64 inst = 0;
+
+	if (ns_proxy) {
+		ns = to_ns_common(ns_proxy->cgroup_ns);
+		inst = ns->inum;
+	}
+	return kcov_remote_handle(KCOV_SUBSYSTEM_IOURING, inst);
+}
+#endif
+
 static __cold struct io_ring_ctx *io_ring_ctx_alloc(struct io_uring_params *p)
 {
 	struct io_ring_ctx *ctx;
@@ -293,6 +308,9 @@ static __cold struct io_ring_ctx *io_ring_ctx_alloc(struct io_uring_params *p)
 	INIT_HLIST_HEAD(&ctx->cancelable_uring_cmd);
 	io_napi_init(ctx);
 	mutex_init(&ctx->mmap_lock);
+#ifdef CONFIG_KCOV
+	ctx->kcov_handle = io_ring_get_kcov_handle();
+#endif
 
 	return ctx;
 
diff --git a/io_uring/io_uring.h b/io_uring/io_uring.h
index e612a66ee80e..cb03df877456 100644
--- a/io_uring/io_uring.h
+++ b/io_uring/io_uring.h
@@ -7,6 +7,7 @@
 #include <linux/resume_user_mode.h>
 #include <linux/poll.h>
 #include <linux/io_uring_types.h>
+#include <linux/kcov.h>
 #include <uapi/linux/eventpoll.h>
 #include "alloc_cache.h"
 #include "io-wq.h"
@@ -581,4 +582,25 @@ static inline bool io_has_work(struct io_ring_ctx *ctx)
 	return test_bit(IO_CHECK_CQ_OVERFLOW_BIT, &ctx->check_cq) ||
 	       io_local_work_pending(ctx);
 }
+
+#ifdef CONFIG_KCOV
+static inline void io_kcov_remote_start(struct io_ring_ctx *ctx)
+{
+	kcov_remote_start(ctx->kcov_handle);
+}
+
+static inline void io_kcov_remote_stop(void)
+{
+	kcov_remote_stop();
+}
+#else
+static inline void io_kcov_remote_start(struct io_ring_ctx *ctx)
+{
+}
+
+static inline void io_kcov_remote_stop(void)
+{
+}
+#endif
+
 #endif
diff --git a/io_uring/sqpoll.c b/io_uring/sqpoll.c
index 46c12afec73e..b244abd37a27 100644
--- a/io_uring/sqpoll.c
+++ b/io_uring/sqpoll.c
@@ -342,19 +342,23 @@ static int io_sq_thread(void *data)
 
 		cap_entries = !list_is_singular(&sqd->ctx_list);
 		list_for_each_entry(ctx, &sqd->ctx_list, sqd_list) {
+			io_kcov_remote_start(ctx);
 			int ret = __io_sq_thread(ctx, sqd, cap_entries, &ist);
 
 			if (!sqt_spin && (ret > 0 || !list_empty(&ctx->iopoll_list)))
 				sqt_spin = true;
+			io_kcov_remote_stop();
 		}
 		if (io_sq_tw(&retry_list, IORING_TW_CAP_ENTRIES_VALUE))
 			sqt_spin = true;
 
 		list_for_each_entry(ctx, &sqd->ctx_list, sqd_list) {
+			io_kcov_remote_start(ctx);
 			if (io_napi(ctx)) {
 				io_sq_start_worktime(&ist);
 				io_napi_sqpoll_busy_poll(ctx);
 			}
+			io_kcov_remote_stop();
 		}
 
 		io_sq_update_worktime(sqd, &ist);
diff --git a/kernel/kcov.c b/kernel/kcov.c
index 0b369e88c7c9..6df04581a126 100644
--- a/kernel/kcov.c
+++ b/kernel/kcov.c
@@ -585,6 +585,8 @@ static inline bool kcov_check_handle(u64 handle, bool common_valid,
 			common_valid : zero_valid;
 	case KCOV_SUBSYSTEM_USB:
 		return uncommon_valid;
+	case KCOV_SUBSYSTEM_IOURING:
+		return uncommon_valid;
 	default:
 		return false;
 	}
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH] io_uring: annotate remote tasks for kcoverage
  2026-05-20 15:39 [PATCH] io_uring: annotate remote tasks for kcoverage Robert Femmer
@ 2026-05-20 17:36 ` Andrey Konovalov
  2026-05-20 20:43   ` [PATCH v2] " Robert Femmer
  0 siblings, 1 reply; 5+ messages in thread
From: Andrey Konovalov @ 2026-05-20 17:36 UTC (permalink / raw)
  To: Robert Femmer; +Cc: io-uring, Jens Axboe, Dmitry Vyukov, kasan-dev

On Wed, May 20, 2026 at 5:41 PM Robert Femmer <robert@fmmr.tech> wrote:
>
> Fuzzers use coverage information to guide generation of test cases
> towards new or interesting code paths. Syzkaller, specifically, makes
> use kcoverage (CONFIG_KCOV). Coverage information is not collected for
> kernel tasks unless annotated by kcov_remote_start and kcov_remote_stop.
> This patch annotates io-uring's work queue and sqpoll tasks.
>
> The value of the handle needs to be passed by userspace when enabling
> remote coverage collection. I chose the cgroup ns inum, because it is
> predictable and flexible enough for consumers to control which group of
> processes should be included for remote coverage collection, should they
> create an instance of io-uring.
>
> Signed-off-by: Robert Femmer <robert@fmmr.tech>
> ---
>  include/linux/io_uring_types.h |  4 ++++
>  include/uapi/linux/kcov.h      |  1 +
>  io_uring/io-wq.c               |  4 ++++
>  io_uring/io_uring.c            | 18 ++++++++++++++++++
>  io_uring/io_uring.h            | 22 ++++++++++++++++++++++
>  io_uring/sqpoll.c              |  4 ++++
>  kernel/kcov.c                  |  2 ++
>  7 files changed, 55 insertions(+)
>
> diff --git a/include/linux/io_uring_types.h b/include/linux/io_uring_types.h
> index 244392026c6d..b92b8e7169ea 100644
> --- a/include/linux/io_uring_types.h
> +++ b/include/linux/io_uring_types.h
> @@ -504,6 +504,10 @@ struct io_ring_ctx {
>         struct io_mapped_region         ring_region;
>         /* used for optimised request parameter and wait argument passing  */
>         struct io_mapped_region         param_region;
> +
> +#ifdef CONFIG_KCOV
> +       u64                             kcov_handle;
> +#endif
>  };
>
>  /*
> diff --git a/include/uapi/linux/kcov.h b/include/uapi/linux/kcov.h
> index ed95dba9fa37..15bbce4569b1 100644
> --- a/include/uapi/linux/kcov.h
> +++ b/include/uapi/linux/kcov.h
> @@ -49,6 +49,7 @@ enum {
>
>  #define KCOV_SUBSYSTEM_COMMON  (0x00ull << 56)
>  #define KCOV_SUBSYSTEM_USB     (0x01ull << 56)
> +#define KCOV_SUBSYSTEM_IOURING (0x02ull << 56)

Hi Robet,

Would it be possible to use the common_handle functionality of KCOV
for io_uring?

The global KCOV handles were not the best design decision. They
arguably make sense for kernel tasks that get created during boot. But
if a kernel task gets spawned as a result of a user task actions, the
common handles are a better approach.

>
>  #define KCOV_SUBSYSTEM_MASK    (0xffull << 56)
>  #define KCOV_INSTANCE_MASK     (0xffffffffull)
> diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c
> index 8cc7b47d3089..bb89d3f4b3dc 100644
> --- a/io_uring/io-wq.c
> +++ b/io_uring/io-wq.c
> @@ -639,6 +639,7 @@ static void io_worker_handle_work(struct io_wq_acct *acct,
>                 /* handle a whole dependent link */
>                 do {
>                         struct io_wq_work *next_hashed, *linked;
> +                       struct io_kiocb *req;
>                         unsigned int work_flags = atomic_read(&work->flags);
>                         unsigned int hash = __io_wq_is_hashed(work_flags)
>                                 ? __io_get_work_hash(work_flags)
> @@ -649,7 +650,10 @@ static void io_worker_handle_work(struct io_wq_acct *acct,
>                         if (do_kill &&
>                             (work_flags & IO_WQ_WORK_UNBOUND))
>                                 atomic_or(IO_WQ_WORK_CANCEL, &work->flags);
> +                       req = container_of(work, struct io_kiocb, work);
> +                       io_kcov_remote_start(req->ctx);
>                         io_wq_submit_work(work);
> +                       io_kcov_remote_stop();
>                         io_assign_current_work(worker, NULL);
>
>                         linked = io_wq_free_work(work);
> diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c
> index 036145ee466c..71478e6ccae4 100644
> --- a/io_uring/io_uring.c
> +++ b/io_uring/io_uring.c
> @@ -222,6 +222,21 @@ static void io_free_alloc_caches(struct io_ring_ctx *ctx)
>         io_rsrc_cache_free(ctx);
>  }
>
> +#ifdef CONFIG_KCOV
> +static __cold u64 io_ring_get_kcov_handle(void)
> +{
> +       struct nsproxy *ns_proxy = current->nsproxy;
> +       struct ns_common *ns;
> +       u64 inst = 0;
> +
> +       if (ns_proxy) {
> +               ns = to_ns_common(ns_proxy->cgroup_ns);
> +               inst = ns->inum;
> +       }
> +       return kcov_remote_handle(KCOV_SUBSYSTEM_IOURING, inst);
> +}
> +#endif
> +
>  static __cold struct io_ring_ctx *io_ring_ctx_alloc(struct io_uring_params *p)
>  {
>         struct io_ring_ctx *ctx;
> @@ -293,6 +308,9 @@ static __cold struct io_ring_ctx *io_ring_ctx_alloc(struct io_uring_params *p)
>         INIT_HLIST_HEAD(&ctx->cancelable_uring_cmd);
>         io_napi_init(ctx);
>         mutex_init(&ctx->mmap_lock);
> +#ifdef CONFIG_KCOV
> +       ctx->kcov_handle = io_ring_get_kcov_handle();
> +#endif
>
>         return ctx;
>
> diff --git a/io_uring/io_uring.h b/io_uring/io_uring.h
> index e612a66ee80e..cb03df877456 100644
> --- a/io_uring/io_uring.h
> +++ b/io_uring/io_uring.h
> @@ -7,6 +7,7 @@
>  #include <linux/resume_user_mode.h>
>  #include <linux/poll.h>
>  #include <linux/io_uring_types.h>
> +#include <linux/kcov.h>
>  #include <uapi/linux/eventpoll.h>
>  #include "alloc_cache.h"
>  #include "io-wq.h"
> @@ -581,4 +582,25 @@ static inline bool io_has_work(struct io_ring_ctx *ctx)
>         return test_bit(IO_CHECK_CQ_OVERFLOW_BIT, &ctx->check_cq) ||
>                io_local_work_pending(ctx);
>  }
> +
> +#ifdef CONFIG_KCOV
> +static inline void io_kcov_remote_start(struct io_ring_ctx *ctx)
> +{
> +       kcov_remote_start(ctx->kcov_handle);
> +}
> +
> +static inline void io_kcov_remote_stop(void)
> +{
> +       kcov_remote_stop();
> +}
> +#else
> +static inline void io_kcov_remote_start(struct io_ring_ctx *ctx)
> +{
> +}
> +
> +static inline void io_kcov_remote_stop(void)
> +{
> +}
> +#endif
> +
>  #endif
> diff --git a/io_uring/sqpoll.c b/io_uring/sqpoll.c
> index 46c12afec73e..b244abd37a27 100644
> --- a/io_uring/sqpoll.c
> +++ b/io_uring/sqpoll.c
> @@ -342,19 +342,23 @@ static int io_sq_thread(void *data)
>
>                 cap_entries = !list_is_singular(&sqd->ctx_list);
>                 list_for_each_entry(ctx, &sqd->ctx_list, sqd_list) {
> +                       io_kcov_remote_start(ctx);
>                         int ret = __io_sq_thread(ctx, sqd, cap_entries, &ist);
>
>                         if (!sqt_spin && (ret > 0 || !list_empty(&ctx->iopoll_list)))
>                                 sqt_spin = true;
> +                       io_kcov_remote_stop();
>                 }
>                 if (io_sq_tw(&retry_list, IORING_TW_CAP_ENTRIES_VALUE))
>                         sqt_spin = true;
>
>                 list_for_each_entry(ctx, &sqd->ctx_list, sqd_list) {
> +                       io_kcov_remote_start(ctx);
>                         if (io_napi(ctx)) {
>                                 io_sq_start_worktime(&ist);
>                                 io_napi_sqpoll_busy_poll(ctx);
>                         }
> +                       io_kcov_remote_stop();
>                 }
>
>                 io_sq_update_worktime(sqd, &ist);
> diff --git a/kernel/kcov.c b/kernel/kcov.c
> index 0b369e88c7c9..6df04581a126 100644
> --- a/kernel/kcov.c
> +++ b/kernel/kcov.c
> @@ -585,6 +585,8 @@ static inline bool kcov_check_handle(u64 handle, bool common_valid,
>                         common_valid : zero_valid;
>         case KCOV_SUBSYSTEM_USB:
>                 return uncommon_valid;
> +       case KCOV_SUBSYSTEM_IOURING:
> +               return uncommon_valid;
>         default:
>                 return false;
>         }
> --
> 2.54.0
>

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v2] io_uring: annotate remote tasks for kcoverage
  2026-05-20 17:36 ` Andrey Konovalov
@ 2026-05-20 20:43   ` Robert Femmer
  2026-05-22 16:23     ` Andrey Konovalov
  0 siblings, 1 reply; 5+ messages in thread
From: Robert Femmer @ 2026-05-20 20:43 UTC (permalink / raw)
  To: io-uring
  Cc: Jens Axboe, Dmitry Vyukov, Andrey Konovalov, kasan-dev,
	Robert Femmer

Fuzzers use coverage information to guide generation of test cases
towards new or interesting code paths. Syzkaller, specifically, makes
use kcoverage (CONFIG_KCOV). Coverage information is not collected for
kernel tasks unless annotated by kcov_remote_start and kcov_remote_stop.
This patch annotates io-uring's work queue and sqpoll tasks.

Signed-off-by: Robert Femmer <robert@fmmr.tech>
---
 include/linux/io_uring_types.h |  4 ++++
 io_uring/io-wq.c               |  4 ++++
 io_uring/io_uring.c            |  3 +++
 io_uring/io_uring.h            | 24 ++++++++++++++++++++++++
 io_uring/sqpoll.c              |  4 ++++
 5 files changed, 39 insertions(+)

diff --git a/include/linux/io_uring_types.h b/include/linux/io_uring_types.h
index 244392026c6d..b92b8e7169ea 100644
--- a/include/linux/io_uring_types.h
+++ b/include/linux/io_uring_types.h
@@ -504,6 +504,10 @@ struct io_ring_ctx {
 	struct io_mapped_region		ring_region;
 	/* used for optimised request parameter and wait argument passing  */
 	struct io_mapped_region		param_region;
+
+#ifdef CONFIG_KCOV
+	u64				kcov_handle;
+#endif
 };
 
 /*
diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c
index 8cc7b47d3089..16af75b1cfe0 100644
--- a/io_uring/io-wq.c
+++ b/io_uring/io-wq.c
@@ -639,6 +639,7 @@ static void io_worker_handle_work(struct io_wq_acct *acct,
 		/* handle a whole dependent link */
 		do {
 			struct io_wq_work *next_hashed, *linked;
+			struct io_kiocb *req;
 			unsigned int work_flags = atomic_read(&work->flags);
 			unsigned int hash = __io_wq_is_hashed(work_flags)
 				? __io_get_work_hash(work_flags)
@@ -649,7 +650,10 @@ static void io_worker_handle_work(struct io_wq_acct *acct,
 			if (do_kill &&
 			    (work_flags & IO_WQ_WORK_UNBOUND))
 				atomic_or(IO_WQ_WORK_CANCEL, &work->flags);
+			req = container_of(work, struct io_kiocb, work);
+			io_kcov_remote_start(req->ctx);
 			io_wq_submit_work(work);
+			io_kcov_remote_stop(req->ctx);
 			io_assign_current_work(worker, NULL);
 
 			linked = io_wq_free_work(work);
diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c
index 036145ee466c..f38b8eca6bbb 100644
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -293,6 +293,9 @@ static __cold struct io_ring_ctx *io_ring_ctx_alloc(struct io_uring_params *p)
 	INIT_HLIST_HEAD(&ctx->cancelable_uring_cmd);
 	io_napi_init(ctx);
 	mutex_init(&ctx->mmap_lock);
+#ifdef CONFIG_KCOV
+	ctx->kcov_handle = current->kcov_handle;
+#endif
 
 	return ctx;
 
diff --git a/io_uring/io_uring.h b/io_uring/io_uring.h
index e612a66ee80e..881d43bd529c 100644
--- a/io_uring/io_uring.h
+++ b/io_uring/io_uring.h
@@ -7,6 +7,7 @@
 #include <linux/resume_user_mode.h>
 #include <linux/poll.h>
 #include <linux/io_uring_types.h>
+#include <linux/kcov.h>
 #include <uapi/linux/eventpoll.h>
 #include "alloc_cache.h"
 #include "io-wq.h"
@@ -581,4 +582,27 @@ static inline bool io_has_work(struct io_ring_ctx *ctx)
 	return test_bit(IO_CHECK_CQ_OVERFLOW_BIT, &ctx->check_cq) ||
 	       io_local_work_pending(ctx);
 }
+
+#ifdef CONFIG_KCOV
+static inline void io_kcov_remote_start(struct io_ring_ctx *ctx)
+{
+	if (ctx->kcov_handle)
+		kcov_remote_start(ctx->kcov_handle);
+}
+
+static inline void io_kcov_remote_stop(struct io_ring_ctx *ctx)
+{
+	if (ctx->kcov_handle)
+		kcov_remote_stop();
+}
+#else
+static inline void io_kcov_remote_start(struct io_ring_ctx *ctx)
+{
+}
+
+static inline void io_kcov_remote_stop(struct io_ring_ctx *ctx)
+{
+}
+#endif
+
 #endif
diff --git a/io_uring/sqpoll.c b/io_uring/sqpoll.c
index 46c12afec73e..8d2876e31acb 100644
--- a/io_uring/sqpoll.c
+++ b/io_uring/sqpoll.c
@@ -342,19 +342,23 @@ static int io_sq_thread(void *data)
 
 		cap_entries = !list_is_singular(&sqd->ctx_list);
 		list_for_each_entry(ctx, &sqd->ctx_list, sqd_list) {
+			io_kcov_remote_start(ctx);
 			int ret = __io_sq_thread(ctx, sqd, cap_entries, &ist);
 
 			if (!sqt_spin && (ret > 0 || !list_empty(&ctx->iopoll_list)))
 				sqt_spin = true;
+			io_kcov_remote_stop(ctx);
 		}
 		if (io_sq_tw(&retry_list, IORING_TW_CAP_ENTRIES_VALUE))
 			sqt_spin = true;
 
 		list_for_each_entry(ctx, &sqd->ctx_list, sqd_list) {
+			io_kcov_remote_start(ctx);
 			if (io_napi(ctx)) {
 				io_sq_start_worktime(&ist);
 				io_napi_sqpoll_busy_poll(ctx);
 			}
+			io_kcov_remote_stop(ctx);
 		}
 
 		io_sq_update_worktime(sqd, &ist);
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH v2] io_uring: annotate remote tasks for kcoverage
  2026-05-20 20:43   ` [PATCH v2] " Robert Femmer
@ 2026-05-22 16:23     ` Andrey Konovalov
  2026-05-26 16:49       ` [PATCH v3] " Robert Femmer
  0 siblings, 1 reply; 5+ messages in thread
From: Andrey Konovalov @ 2026-05-22 16:23 UTC (permalink / raw)
  To: Robert Femmer; +Cc: io-uring, Jens Axboe, Dmitry Vyukov, kasan-dev, Jann Horn

On Wed, May 20, 2026 at 10:44 PM Robert Femmer <robert@fmmr.tech> wrote:
>
> Fuzzers use coverage information to guide generation of test cases
> towards new or interesting code paths. Syzkaller, specifically, makes
> use kcoverage (CONFIG_KCOV). Coverage information is not collected for
> kernel tasks unless annotated by kcov_remote_start and kcov_remote_stop.
> This patch annotates io-uring's work queue and sqpoll tasks.
>
> Signed-off-by: Robert Femmer <robert@fmmr.tech>
> ---
>  include/linux/io_uring_types.h |  4 ++++
>  io_uring/io-wq.c               |  4 ++++
>  io_uring/io_uring.c            |  3 +++
>  io_uring/io_uring.h            | 24 ++++++++++++++++++++++++
>  io_uring/sqpoll.c              |  4 ++++
>  5 files changed, 39 insertions(+)
>
> diff --git a/include/linux/io_uring_types.h b/include/linux/io_uring_types.h
> index 244392026c6d..b92b8e7169ea 100644
> --- a/include/linux/io_uring_types.h
> +++ b/include/linux/io_uring_types.h
> @@ -504,6 +504,10 @@ struct io_ring_ctx {
>         struct io_mapped_region         ring_region;
>         /* used for optimised request parameter and wait argument passing  */
>         struct io_mapped_region         param_region;
> +
> +#ifdef CONFIG_KCOV
> +       u64                             kcov_handle;
> +#endif

Jann recently sent a patch that added kcov_common_handle_id, I think
you can base your code on it and use that helper struct here.

https://lore.kernel.org/all/20260430-kcov-refactor-common-handle-v1-1-23a0c7a0ba38@google.com/

>  };
>
>  /*
> diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c
> index 8cc7b47d3089..16af75b1cfe0 100644
> --- a/io_uring/io-wq.c
> +++ b/io_uring/io-wq.c
> @@ -639,6 +639,7 @@ static void io_worker_handle_work(struct io_wq_acct *acct,
>                 /* handle a whole dependent link */
>                 do {
>                         struct io_wq_work *next_hashed, *linked;
> +                       struct io_kiocb *req;
>                         unsigned int work_flags = atomic_read(&work->flags);
>                         unsigned int hash = __io_wq_is_hashed(work_flags)
>                                 ? __io_get_work_hash(work_flags)
> @@ -649,7 +650,10 @@ static void io_worker_handle_work(struct io_wq_acct *acct,
>                         if (do_kill &&
>                             (work_flags & IO_WQ_WORK_UNBOUND))
>                                 atomic_or(IO_WQ_WORK_CANCEL, &work->flags);
> +                       req = container_of(work, struct io_kiocb, work);
> +                       io_kcov_remote_start(req->ctx);

And also use kcov_remote_start_common() here.

>                         io_wq_submit_work(work);
> +                       io_kcov_remote_stop(req->ctx);
>                         io_assign_current_work(worker, NULL);
>
>                         linked = io_wq_free_work(work);
> diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c
> index 036145ee466c..f38b8eca6bbb 100644
> --- a/io_uring/io_uring.c
> +++ b/io_uring/io_uring.c
> @@ -293,6 +293,9 @@ static __cold struct io_ring_ctx *io_ring_ctx_alloc(struct io_uring_params *p)
>         INIT_HLIST_HEAD(&ctx->cancelable_uring_cmd);
>         io_napi_init(ctx);
>         mutex_init(&ctx->mmap_lock);
> +#ifdef CONFIG_KCOV
> +       ctx->kcov_handle = current->kcov_handle;
> +#endif
>
>         return ctx;
>
> diff --git a/io_uring/io_uring.h b/io_uring/io_uring.h
> index e612a66ee80e..881d43bd529c 100644
> --- a/io_uring/io_uring.h
> +++ b/io_uring/io_uring.h
> @@ -7,6 +7,7 @@
>  #include <linux/resume_user_mode.h>
>  #include <linux/poll.h>
>  #include <linux/io_uring_types.h>
> +#include <linux/kcov.h>
>  #include <uapi/linux/eventpoll.h>
>  #include "alloc_cache.h"
>  #include "io-wq.h"
> @@ -581,4 +582,27 @@ static inline bool io_has_work(struct io_ring_ctx *ctx)
>         return test_bit(IO_CHECK_CQ_OVERFLOW_BIT, &ctx->check_cq) ||
>                io_local_work_pending(ctx);
>  }
> +
> +#ifdef CONFIG_KCOV
> +static inline void io_kcov_remote_start(struct io_ring_ctx *ctx)
> +{
> +       if (ctx->kcov_handle)
> +               kcov_remote_start(ctx->kcov_handle);
> +}
> +
> +static inline void io_kcov_remote_stop(struct io_ring_ctx *ctx)
> +{
> +       if (ctx->kcov_handle)
> +               kcov_remote_stop();
> +}
> +#else
> +static inline void io_kcov_remote_start(struct io_ring_ctx *ctx)
> +{
> +}
> +
> +static inline void io_kcov_remote_stop(struct io_ring_ctx *ctx)
> +{
> +}
> +#endif
> +
>  #endif
> diff --git a/io_uring/sqpoll.c b/io_uring/sqpoll.c
> index 46c12afec73e..8d2876e31acb 100644
> --- a/io_uring/sqpoll.c
> +++ b/io_uring/sqpoll.c
> @@ -342,19 +342,23 @@ static int io_sq_thread(void *data)
>
>                 cap_entries = !list_is_singular(&sqd->ctx_list);
>                 list_for_each_entry(ctx, &sqd->ctx_list, sqd_list) {
> +                       io_kcov_remote_start(ctx);
>                         int ret = __io_sq_thread(ctx, sqd, cap_entries, &ist);
>
>                         if (!sqt_spin && (ret > 0 || !list_empty(&ctx->iopoll_list)))
>                                 sqt_spin = true;
> +                       io_kcov_remote_stop(ctx);
>                 }
>                 if (io_sq_tw(&retry_list, IORING_TW_CAP_ENTRIES_VALUE))
>                         sqt_spin = true;
>
>                 list_for_each_entry(ctx, &sqd->ctx_list, sqd_list) {
> +                       io_kcov_remote_start(ctx);
>                         if (io_napi(ctx)) {
>                                 io_sq_start_worktime(&ist);
>                                 io_napi_sqpoll_busy_poll(ctx);
>                         }
> +                       io_kcov_remote_stop(ctx);
>                 }
>
>                 io_sq_update_worktime(sqd, &ist);
> --
> 2.54.0
>
> --
> You received this message because you are subscribed to the Google Groups "kasan-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to kasan-dev+unsubscribe@googlegroups.com.
> To view this discussion visit https://groups.google.com/d/msgid/kasan-dev/20260520204303.558392-2-robert%40fmmr.tech.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v3] io_uring: annotate remote tasks for kcoverage
  2026-05-22 16:23     ` Andrey Konovalov
@ 2026-05-26 16:49       ` Robert Femmer
  0 siblings, 0 replies; 5+ messages in thread
From: Robert Femmer @ 2026-05-26 16:49 UTC (permalink / raw)
  To: io-uring
  Cc: Jens Axboe, Dmitry Vyukov, Andrey Konovalov, kasan-dev,
	Robert Femmer

Fuzzers use coverage information to guide generation of test cases
towards new or interesting code paths. Syzkaller, specifically, makes
use kcoverage (CONFIG_KCOV). Coverage information is not collected for
kernel tasks unless annotated by kcov_remote_start and kcov_remote_stop.
This patch annotates io-uring's work queue and sqpoll tasks.

Depends-on: 20260430-kcov-refactor-common-handle-v1-1-23a0c7a0ba38@google.com
Signed-off-by: Robert Femmer <robert@fmmr.tech>
---
 include/linux/io_uring_types.h | 2 ++
 io_uring/io-wq.c               | 4 ++++
 io_uring/io_uring.c            | 1 +
 io_uring/io_uring.h            | 2 ++
 io_uring/sqpoll.c              | 4 ++++
 5 files changed, 13 insertions(+)

diff --git a/include/linux/io_uring_types.h b/include/linux/io_uring_types.h
index 244392026c6d..b6590b2b350c 100644
--- a/include/linux/io_uring_types.h
+++ b/include/linux/io_uring_types.h
@@ -504,6 +504,8 @@ struct io_ring_ctx {
 	struct io_mapped_region		ring_region;
 	/* used for optimised request parameter and wait argument passing  */
 	struct io_mapped_region		param_region;
+
+	struct kcov_common_handle_id	kcov_handle;
 };
 
 /*
diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c
index 8cc7b47d3089..9ade4c4f4983 100644
--- a/io_uring/io-wq.c
+++ b/io_uring/io-wq.c
@@ -639,6 +639,7 @@ static void io_worker_handle_work(struct io_wq_acct *acct,
 		/* handle a whole dependent link */
 		do {
 			struct io_wq_work *next_hashed, *linked;
+			struct io_kiocb *req;
 			unsigned int work_flags = atomic_read(&work->flags);
 			unsigned int hash = __io_wq_is_hashed(work_flags)
 				? __io_get_work_hash(work_flags)
@@ -649,7 +650,10 @@ static void io_worker_handle_work(struct io_wq_acct *acct,
 			if (do_kill &&
 			    (work_flags & IO_WQ_WORK_UNBOUND))
 				atomic_or(IO_WQ_WORK_CANCEL, &work->flags);
+			req = container_of(work, struct io_kiocb, work);
+			kcov_remote_start_common(req->ctx->kcov_handle);
 			io_wq_submit_work(work);
+			kcov_remote_stop();
 			io_assign_current_work(worker, NULL);
 
 			linked = io_wq_free_work(work);
diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c
index 103b6c88f252..89cb649944d9 100644
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -293,6 +293,7 @@ static __cold struct io_ring_ctx *io_ring_ctx_alloc(struct io_uring_params *p)
 	INIT_HLIST_HEAD(&ctx->cancelable_uring_cmd);
 	io_napi_init(ctx);
 	mutex_init(&ctx->mmap_lock);
+	ctx->kcov_handle = kcov_common_handle();
 
 	return ctx;
 
diff --git a/io_uring/io_uring.h b/io_uring/io_uring.h
index e612a66ee80e..7226fbbbf9f0 100644
--- a/io_uring/io_uring.h
+++ b/io_uring/io_uring.h
@@ -7,6 +7,7 @@
 #include <linux/resume_user_mode.h>
 #include <linux/poll.h>
 #include <linux/io_uring_types.h>
+#include <linux/kcov.h>
 #include <uapi/linux/eventpoll.h>
 #include "alloc_cache.h"
 #include "io-wq.h"
@@ -581,4 +582,5 @@ static inline bool io_has_work(struct io_ring_ctx *ctx)
 	return test_bit(IO_CHECK_CQ_OVERFLOW_BIT, &ctx->check_cq) ||
 	       io_local_work_pending(ctx);
 }
+
 #endif
diff --git a/io_uring/sqpoll.c b/io_uring/sqpoll.c
index 46c12afec73e..c7b78ea98587 100644
--- a/io_uring/sqpoll.c
+++ b/io_uring/sqpoll.c
@@ -342,19 +342,23 @@ static int io_sq_thread(void *data)
 
 		cap_entries = !list_is_singular(&sqd->ctx_list);
 		list_for_each_entry(ctx, &sqd->ctx_list, sqd_list) {
+			kcov_remote_start_common(ctx->kcov_handle);
 			int ret = __io_sq_thread(ctx, sqd, cap_entries, &ist);
 
 			if (!sqt_spin && (ret > 0 || !list_empty(&ctx->iopoll_list)))
 				sqt_spin = true;
+			kcov_remote_stop();
 		}
 		if (io_sq_tw(&retry_list, IORING_TW_CAP_ENTRIES_VALUE))
 			sqt_spin = true;
 
 		list_for_each_entry(ctx, &sqd->ctx_list, sqd_list) {
+			kcov_remote_start_common(ctx->kcov_handle);
 			if (io_napi(ctx)) {
 				io_sq_start_worktime(&ist);
 				io_napi_sqpoll_busy_poll(ctx);
 			}
+			kcov_remote_stop();
 		}
 
 		io_sq_update_worktime(sqd, &ist);
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-05-26 16:50 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-20 15:39 [PATCH] io_uring: annotate remote tasks for kcoverage Robert Femmer
2026-05-20 17:36 ` Andrey Konovalov
2026-05-20 20:43   ` [PATCH v2] " Robert Femmer
2026-05-22 16:23     ` Andrey Konovalov
2026-05-26 16:49       ` [PATCH v3] " Robert Femmer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox