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
next prev 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®