Two threads calling KCOV_DF_INIT_TRACK concurrently could both observe
df->area == NULL, drop the lock to allocate, and then both assign their
allocation to df->area, leaking one buffer.

Fix by rechecking df->area after re-acquiring the lock. If another
thread won the race, free the allocation and return -EBUSY. This
matches the pattern used by KCOV_INIT_TRACE in kernel/kcov.c.

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]>
---
 kernel/kcov_dataflow.c | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/kernel/kcov_dataflow.c b/kernel/kcov_dataflow.c
index 721f742cbfe5..df7e8bf70bfa 100644
--- a/kernel/kcov_dataflow.c
+++ b/kernel/kcov_dataflow.c
@@ -268,11 +268,20 @@ static long kcov_df_ioctl(struct file *filep, unsigned 
int cmd, unsigned long ar
                        break;
                }
                spin_unlock_irqrestore(&df->lock, flags);
-               df->area = vmalloc_user(size * sizeof(u64));
-               if (!df->area)
-                       return -ENOMEM;
-               spin_lock_irqsave(&df->lock, flags);
-               df->size = size;
+               {
+                       void *area = vmalloc_user(size * sizeof(u64));
+
+                       if (!area)
+                               return -ENOMEM;
+                       spin_lock_irqsave(&df->lock, flags);
+                       if (df->area) {
+                               spin_unlock_irqrestore(&df->lock, flags);
+                               vfree(area);
+                               return -EBUSY;
+                       }
+                       df->area = area;
+                       df->size = size;
+               }
                break;
 
        case KCOV_DF_ENABLE:

-- 
2.43.0


Reply via email to