dup_task_struct() copies the parent task_struct byte-for-byte. Without
explicitly clearing the dataflow fields, a forked child inherits the
parent's kcov_df_enabled flag and buffer pointer, leading to two tasks
writing to the same buffer and a potential use-after-free if the parent
closes the trace file.

Add kcov_dataflow_task_init() in kernel/kcov_dataflow.c and call it from
kernel/fork.c alongside kcov_task_init(), matching how kcov_stop() clears
the legacy kcov fields during fork.

Reported-by: sashiko-bot <[email protected]>
Closes: 
https://sashiko.dev/#/patchset/20260603-kcov-dataflow-next-20260603-v2-0-fee0939de2c4%40est.tech
Signed-off-by: Yunseong Kim <[email protected]>
---
 include/linux/kcov.h   |  6 ++++++
 kernel/fork.c          |  1 +
 kernel/kcov_dataflow.c | 10 ++++++++++
 3 files changed, 17 insertions(+)

diff --git a/include/linux/kcov.h b/include/linux/kcov.h
index 895b761b2db1..e9822b02982b 100644
--- a/include/linux/kcov.h
+++ b/include/linux/kcov.h
@@ -28,6 +28,12 @@ enum kcov_mode {
 void kcov_task_init(struct task_struct *t);
 void kcov_task_exit(struct task_struct *t);
 
+#if defined(CONFIG_KCOV_DATAFLOW_ARGS) || defined(CONFIG_KCOV_DATAFLOW_RET)
+void kcov_dataflow_task_init(struct task_struct *t);
+#else
+static inline void kcov_dataflow_task_init(struct task_struct *t) {}
+#endif
+
 #define kcov_prepare_switch(t)                 \
 do {                                           \
        (t)->kcov_mode |= KCOV_IN_CTXSW;        \
diff --git a/kernel/fork.c b/kernel/fork.c
index 892a95214c54..a5741de07979 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -980,6 +980,7 @@ static struct task_struct *dup_task_struct(struct 
task_struct *orig, int node)
        tsk->worker_private = NULL;
 
        kcov_task_init(tsk);
+       kcov_dataflow_task_init(tsk);
        kmsan_task_create(tsk);
        kmap_local_fork(tsk);
 
diff --git a/kernel/kcov_dataflow.c b/kernel/kcov_dataflow.c
index 27587b8ceeab..7cfe2495275a 100644
--- a/kernel/kcov_dataflow.c
+++ b/kernel/kcov_dataflow.c
@@ -32,6 +32,7 @@
 #include <linux/debugfs.h>
 #include <linux/uaccess.h>
 #include <linux/refcount.h>
+#include <linux/kcov.h>
 
 #define KCOV_DF_TYPE_ENTRY     0xE0000000ULL
 #define KCOV_DF_TYPE_RET       0xF0000000ULL
@@ -186,6 +187,15 @@ __sanitizer_cov_trace_ret(u64 pc, u32 ret_size, void 
*ret_val,
 EXPORT_SYMBOL(__sanitizer_cov_trace_ret);
 #endif
 
+/* Called from kernel/fork.c to clear inherited state. */
+void kcov_dataflow_task_init(struct task_struct *t)
+{
+       t->kcov_df_area = NULL;
+       t->kcov_df_size = 0;
+       t->kcov_df_seq = 0;
+       t->kcov_df_enabled = false;
+}
+
 /* File operations for /sys/kernel/debug/kcov_dataflow */
 
 static int kcov_df_open(struct inode *inode, struct file *filep)

-- 
2.43.0


Reply via email to