mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Jann Horn <jannh@google.com>
To: Dmitry Vyukov <dvyukov@google.com>,
	 Andrey Konovalov <andreyknvl@gmail.com>
Cc: Nathan Chancellor <nathan@kernel.org>,
	 Nick Desaulniers <nick.desaulniers+lkml@gmail.com>,
	 Bill Wendling <morbo@google.com>,
	Justin Stitt <justinstitt@google.com>,
	 linux-kernel@vger.kernel.org, kasan-dev@googlegroups.com,
	 llvm@lists.linux.dev, Jann Horn <jannh@google.com>
Subject: [PATCH 3/3] kcov: introduce extended PC coverage collection mode
Date: Wed, 11 Mar 2026 22:06:16 +0100	[thread overview]
Message-ID: <20260311-kcov-extrecord-v1-3-68f03c4a05ad@google.com> (raw)
In-Reply-To: <20260311-kcov-extrecord-v1-0-68f03c4a05ad@google.com>

This is the second half of CONFIG_KCOV_EXT_RECORDS.

Introduce a new KCOV mode KCOV_TRACE_PC_EXT which replaces the upper 8 bits
of recorded instruction pointers with metadata. For now, userspace can use
this metadata to distinguish three types of records:

 - function entry
 - function exit
 - normal basic block inside the function

Signed-off-by: Jann Horn <jannh@google.com>
---
 include/linux/sched.h     |  6 ++++--
 include/uapi/linux/kcov.h | 12 ++++++++++++
 kernel/kcov.c             | 46 +++++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 57 insertions(+), 7 deletions(-)

diff --git a/include/linux/sched.h b/include/linux/sched.h
index a7b4a980eb2f..9a297d2d2abc 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1519,8 +1519,10 @@ struct task_struct {
 	int				kcov_sequence;
 
 	/* Collect coverage from softirq context: */
-	unsigned int			kcov_softirq;
-#endif
+	unsigned int			kcov_softirq : 1;
+	/* Emit KCOV records in extended format: */
+	unsigned int			kcov_ext_format : 1;
+#endif /* CONFIG_KCOV */
 
 #ifdef CONFIG_MEMCG_V1
 	struct mem_cgroup		*memcg_in_oom;
diff --git a/include/uapi/linux/kcov.h b/include/uapi/linux/kcov.h
index ed95dba9fa37..8d8a233bd61f 100644
--- a/include/uapi/linux/kcov.h
+++ b/include/uapi/linux/kcov.h
@@ -35,8 +35,20 @@ enum {
 	KCOV_TRACE_PC = 0,
 	/* Collecting comparison operands mode. */
 	KCOV_TRACE_CMP = 1,
+	/*
+	 * Extended PC coverage collection mode.
+	 * In this mode, the top byte of the PC is replaced with flag bits
+	 * (KCOV_RECORDFLAG_*).
+	 */
+	KCOV_TRACE_PC_EXT = 2,
 };
 
+#define KCOV_RECORD_IP_MASK         0x00ffffffffffffff
+#define KCOV_RECORDFLAG_TYPEMASK    0xf000000000000000
+#define KCOV_RECORDFLAG_TYPE_NORMAL 0xf000000000000000
+#define KCOV_RECORDFLAG_TYPE_ENTRY  0x0000000000000000
+#define KCOV_RECORDFLAG_TYPE_EXIT   0x1000000000000000
+
 /*
  * The format for the types of collected comparisons.
  *
diff --git a/kernel/kcov.c b/kernel/kcov.c
index 2cc48b65384b..3482044a7bd5 100644
--- a/kernel/kcov.c
+++ b/kernel/kcov.c
@@ -71,6 +71,8 @@ struct kcov {
 	 * kcov_remote_stop(), see the comment there.
 	 */
 	int			sequence;
+	/* Whether emitted records should have type bits. */
+	unsigned int kcov_ext_format : 1 __guarded_by(&lock);
 };
 
 struct kcov_remote_area {
@@ -97,6 +99,7 @@ struct kcov_percpu_data {
 	void			*saved_area;
 	struct kcov		*saved_kcov;
 	int			saved_sequence;
+	unsigned int		saved_kcov_ext_format : 1;
 };
 
 static DEFINE_PER_CPU(struct kcov_percpu_data, kcov_percpu_data) = {
@@ -235,6 +238,12 @@ static void notrace kcov_add_pc_record(unsigned long record)
  */
 void notrace __sanitizer_cov_trace_pc(void)
 {
+	/*
+	 * No bitops are needed here for setting the record type because
+	 * KCOV_RECORDFLAG_TYPE_NORMAL has the high bits set.
+	 * This relies on userspace not caring about the rest of the top byte
+	 * for KCOV_RECORDFLAG_TYPE_NORMAL records.
+	 */
 	kcov_add_pc_record(canonicalize_ip(_RET_IP_));
 }
 EXPORT_SYMBOL(__sanitizer_cov_trace_pc);
@@ -244,10 +253,26 @@ void notrace __sanitizer_cov_trace_pc_entry(void)
 {
 	unsigned long record = canonicalize_ip(_RET_IP_);
 
+	/*
+	 * This hook replaces __sanitizer_cov_trace_pc() for the function entry
+	 * basic block; it should still emit a record even in classic kcov mode.
+	 */
+	if (current->kcov_ext_format)
+		record = (record & KCOV_RECORD_IP_MASK) | KCOV_RECORDFLAG_TYPE_ENTRY;
 	kcov_add_pc_record(record);
 }
 void notrace __sanitizer_cov_trace_pc_exit(void)
 {
+	unsigned long record;
+
+	/*
+	 * Unlike __sanitizer_cov_trace_pc_entry(), this PC should only be
+	 * reported in extended mode.
+	 */
+	if (!current->kcov_ext_format)
+		return;
+	record = (canonicalize_ip(_RET_IP_) & KCOV_RECORD_IP_MASK) | KCOV_RECORDFLAG_TYPE_EXIT;
+	kcov_add_pc_record(record);
 }
 #endif
 
@@ -371,7 +396,7 @@ EXPORT_SYMBOL(__sanitizer_cov_trace_switch);
 
 static void kcov_start(struct task_struct *t, struct kcov *kcov,
 			unsigned int size, void *area, enum kcov_mode mode,
-			int sequence)
+			int sequence, unsigned int kcov_ext_format)
 {
 	kcov_debug("t = %px, size = %u, area = %px\n", t, size, area);
 	t->kcov = kcov;
@@ -379,6 +404,7 @@ static void kcov_start(struct task_struct *t, struct kcov *kcov,
 	t->kcov_size = size;
 	t->kcov_area = area;
 	t->kcov_sequence = sequence;
+	t->kcov_ext_format = kcov_ext_format;
 	/* See comment in check_kcov_mode(). */
 	barrier();
 	WRITE_ONCE(t->kcov_mode, mode);
@@ -398,6 +424,7 @@ static void kcov_task_reset(struct task_struct *t)
 	kcov_stop(t);
 	t->kcov_sequence = 0;
 	t->kcov_handle = 0;
+	t->kcov_ext_format = 0;
 }
 
 void kcov_task_init(struct task_struct *t)
@@ -570,6 +597,8 @@ static int kcov_get_mode(unsigned long arg)
 #else
 		return -ENOTSUPP;
 #endif
+	else if (arg == KCOV_TRACE_PC_EXT)
+		return IS_ENABLED(CONFIG_KCOV_EXT_RECORDS) ? KCOV_MODE_TRACE_PC : -ENOTSUPP;
 	else
 		return -EINVAL;
 }
@@ -636,8 +665,9 @@ static int kcov_ioctl_locked(struct kcov *kcov, unsigned int cmd,
 			return mode;
 		kcov_fault_in_area(kcov);
 		kcov->mode = mode;
+		kcov->kcov_ext_format = (arg == KCOV_TRACE_PC_EXT);
 		kcov_start(t, kcov, kcov->size, kcov->area, kcov->mode,
-				kcov->sequence);
+				kcov->sequence, kcov->kcov_ext_format);
 		kcov->t = t;
 		/* Put either in kcov_task_exit() or in KCOV_DISABLE. */
 		kcov_get(kcov);
@@ -668,7 +698,8 @@ static int kcov_ioctl_locked(struct kcov *kcov, unsigned int cmd,
 			return -EINVAL;
 		kcov->mode = mode;
 		t->kcov = kcov;
-	        t->kcov_mode = KCOV_MODE_REMOTE;
+		t->kcov_mode = KCOV_MODE_REMOTE;
+		kcov->kcov_ext_format = (remote_arg->trace_mode == KCOV_TRACE_PC_EXT);
 		kcov->t = t;
 		kcov->remote = true;
 		kcov->remote_size = remote_arg->area_size;
@@ -853,6 +884,7 @@ static void kcov_remote_softirq_start(struct task_struct *t)
 		data->saved_area = t->kcov_area;
 		data->saved_sequence = t->kcov_sequence;
 		data->saved_kcov = t->kcov;
+		data->saved_kcov_ext_format = t->kcov_ext_format;
 		kcov_stop(t);
 	}
 }
@@ -865,12 +897,14 @@ static void kcov_remote_softirq_stop(struct task_struct *t)
 	if (data->saved_kcov) {
 		kcov_start(t, data->saved_kcov, data->saved_size,
 				data->saved_area, data->saved_mode,
-				data->saved_sequence);
+				data->saved_sequence,
+				data->saved_kcov_ext_format);
 		data->saved_mode = 0;
 		data->saved_size = 0;
 		data->saved_area = NULL;
 		data->saved_sequence = 0;
 		data->saved_kcov = NULL;
+		data->saved_kcov_ext_format = 0;
 	}
 }
 
@@ -884,6 +918,7 @@ void kcov_remote_start(u64 handle)
 	unsigned int size;
 	int sequence;
 	unsigned long flags;
+	unsigned int kcov_ext_format;
 
 	if (WARN_ON(!kcov_check_handle(handle, true, true, true)))
 		return;
@@ -930,6 +965,7 @@ void kcov_remote_start(u64 handle)
 	 * acquired _after_ kcov->lock elsewhere.
 	 */
 	mode = context_unsafe(kcov->mode);
+	kcov_ext_format = context_unsafe(kcov->kcov_ext_format);
 	sequence = kcov->sequence;
 	if (in_task()) {
 		size = kcov->remote_size;
@@ -958,7 +994,7 @@ void kcov_remote_start(u64 handle)
 		kcov_remote_softirq_start(t);
 		t->kcov_softirq = 1;
 	}
-	kcov_start(t, kcov, size, area, mode, sequence);
+	kcov_start(t, kcov, size, area, mode, sequence, kcov_ext_format);
 
 	local_unlock_irqrestore(&kcov_percpu_data.lock, flags);
 

-- 
2.53.0.473.g4a7958ca14-goog


  parent reply	other threads:[~2026-03-11 21:06 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-11 21:06 [PATCH 0/3] KCOV function entry/exit records Jann Horn
2026-03-11 21:06 ` [PATCH 1/3] sched: Ensure matching stack state for kcov disable/enable on switch Jann Horn
2026-03-11 21:06 ` [PATCH 2/3] kcov: wire up compiler instrumentation for CONFIG_KCOV_EXT_RECORDS Jann Horn
2026-03-13  7:53   ` Dmitry Vyukov
2026-03-13  7:59     ` Dmitry Vyukov
2026-03-13 12:40       ` Jann Horn
2026-03-13 12:40     ` Jann Horn
2026-03-11 21:06 ` Jann Horn [this message]
2026-03-13  7:58   ` [PATCH 3/3] kcov: introduce extended PC coverage collection mode Dmitry Vyukov
2026-03-13 12:31     ` Jann Horn
2026-03-16 16:50     ` Alexander Potapenko
2026-03-11 22:57 ` [PATCH 0/3] KCOV function entry/exit records Peter Zijlstra
2026-03-12 11:44   ` Jann Horn

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=20260311-kcov-extrecord-v1-3-68f03c4a05ad@google.com \
    --to=jannh@google.com \
    --cc=andreyknvl@gmail.com \
    --cc=dvyukov@google.com \
    --cc=justinstitt@google.com \
    --cc=kasan-dev@googlegroups.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=llvm@lists.linux.dev \
    --cc=morbo@google.com \
    --cc=nathan@kernel.org \
    --cc=nick.desaulniers+lkml@gmail.com \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®