[PATCH] io_uring: annotate remote tasks for kcoverage

0 views
Skip to first unread message

Robert Femmer

unread,
11:41 AM (3 hours ago) 11:41 AM
to io-u...@vger.kernel.org, Jens Axboe, Dmitry Vyukov, Andrey Konovalov, kasa...@googlegroups.com, 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 <rob...@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

Andrey Konovalov

unread,
1:36 PM (2 hours ago) 1:36 PM
to Robert Femmer, io-u...@vger.kernel.org, Jens Axboe, Dmitry Vyukov, kasa...@googlegroups.com
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.
Reply all
Reply to author
Forward
0 new messages