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 Closes: https://sashiko.dev/#/patchset/20260603-kcov-dataflow-next-20260603-v2-0-fee0939de2c4%40est.tech Signed-off-by: Yunseong Kim --- 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 #include #include +#include #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