rcutorture: Fix rcu_torture_one_read()/rcu_torture_writer() data race
authorPaul E. McKenney <paulmck@kernel.org>
Sat, 21 Dec 2019 18:41:48 +0000 (10:41 -0800)
committerPaul E. McKenney <paulmck@kernel.org>
Fri, 21 Feb 2020 00:03:31 +0000 (16:03 -0800)
The ->rtort_pipe_count field in the rcu_torture structure checks for
too-short grace periods, and is therefore read by rcutorture's readers
while being updated by rcutorture's writers.  This commit therefore
adds the needed READ_ONCE() and WRITE_ONCE() invocations.

This data race was reported by KCSAN.  Not appropriate for backporting
due to failure being unlikely and due to this being rcutorture.

Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
kernel/rcu/rcutorture.c

index 5efd9503df56caa47a530610b9f3b1ca26cfc657..edd97465a0f778842d98d9f57928e03eac54f0ab 100644 (file)
@@ -375,11 +375,12 @@ rcu_torture_pipe_update_one(struct rcu_torture *rp)
 {
        int i;
 
-       i = rp->rtort_pipe_count;
+       i = READ_ONCE(rp->rtort_pipe_count);
        if (i > RCU_TORTURE_PIPE_LEN)
                i = RCU_TORTURE_PIPE_LEN;
        atomic_inc(&rcu_torture_wcount[i]);
-       if (++rp->rtort_pipe_count >= RCU_TORTURE_PIPE_LEN) {
+       WRITE_ONCE(rp->rtort_pipe_count, i + 1);
+       if (rp->rtort_pipe_count >= RCU_TORTURE_PIPE_LEN) {
                rp->rtort_mbtest = 0;
                return true;
        }
@@ -1015,7 +1016,8 @@ rcu_torture_writer(void *arg)
                        if (i > RCU_TORTURE_PIPE_LEN)
                                i = RCU_TORTURE_PIPE_LEN;
                        atomic_inc(&rcu_torture_wcount[i]);
-                       old_rp->rtort_pipe_count++;
+                       WRITE_ONCE(old_rp->rtort_pipe_count,
+                                  old_rp->rtort_pipe_count + 1);
                        switch (synctype[torture_random(&rand) % nsynctypes]) {
                        case RTWS_DEF_FREE:
                                rcu_torture_writer_state = RTWS_DEF_FREE;
@@ -1291,7 +1293,7 @@ static bool rcu_torture_one_read(struct torture_random_state *trsp)
                atomic_inc(&n_rcu_torture_mberror);
        rtrsp = rcutorture_loop_extend(&readstate, trsp, rtrsp);
        preempt_disable();
-       pipe_count = p->rtort_pipe_count;
+       pipe_count = READ_ONCE(p->rtort_pipe_count);
        if (pipe_count > RCU_TORTURE_PIPE_LEN) {
                /* Should not happen, but... */
                pipe_count = RCU_TORTURE_PIPE_LEN;