summaryrefslogtreecommitdiff
path: root/kernel/kcsan
diff options
context:
space:
mode:
authorMarco Elver <elver@google.com>2020-02-10 15:56:39 +0100
committerIngo Molnar <mingo@kernel.org>2020-03-21 09:43:41 +0100
commit3a5b45e5031fa395ab6e53545fb726b2d5b104f0 (patch)
tree311b9c0dc636cd1f33a077372e14ed9153014bd1 /kernel/kcsan
parent80d4c4775216602ccdc9e761ce251c8451d0c6ca (diff)
kcsan: Fix misreporting if concurrent races on same address
If there are at least 4 threads racing on the same address, it can happen that one of the readers may observe another matching reader in other_info. To avoid locking up, we have to consume 'other_info' regardless, but skip the report. See the added comment for more details. Signed-off-by: Marco Elver <elver@google.com> Signed-off-by: Paul E. McKenney <paulmck@kernel.org> Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel/kcsan')
-rw-r--r--kernel/kcsan/report.c38
1 files changed, 38 insertions, 0 deletions
diff --git a/kernel/kcsan/report.c b/kernel/kcsan/report.c
index 3bc590e6be7e..abf6852dff72 100644
--- a/kernel/kcsan/report.c
+++ b/kernel/kcsan/report.c
@@ -422,6 +422,44 @@ retry:
return false;
}
+ access_type |= other_info.access_type;
+ if ((access_type & KCSAN_ACCESS_WRITE) == 0) {
+ /*
+ * While the address matches, this is not the other_info
+ * from the thread that consumed our watchpoint, since
+ * neither this nor the access in other_info is a write.
+ * It is invalid to continue with the report, since we
+ * only have information about reads.
+ *
+ * This can happen due to concurrent races on the same
+ * address, with at least 4 threads. To avoid locking up
+ * other_info and all other threads, we have to consume
+ * it regardless.
+ *
+ * A concrete case to illustrate why we might lock up if
+ * we do not consume other_info:
+ *
+ * We have 4 threads, all accessing the same address
+ * (or matching address ranges). Assume the following
+ * watcher and watchpoint consumer pairs:
+ * write1-read1, read2-write2. The first to populate
+ * other_info is write2, however, write1 consumes it,
+ * resulting in a report of write1-write2. This report
+ * is valid, however, now read1 populates other_info;
+ * read2-read1 is an invalid conflict, yet, no other
+ * conflicting access is left. Therefore, we must
+ * consume read1's other_info.
+ *
+ * Since this case is assumed to be rare, it is
+ * reasonable to omit this report: one of the other
+ * reports includes information about the same shared
+ * data, and at this point the likelihood that we
+ * re-report the same race again is high.
+ */
+ release_report(flags, KCSAN_REPORT_RACE_SIGNAL);
+ return false;
+ }
+
/*
* Matching & usable access in other_info: keep other_info_lock
* locked, as this thread consumes it to print the full report;