ptp: annotate data-race around q->head and q->tail
authorEric Dumazet <edumazet@google.com>
Thu, 9 Nov 2023 17:48:59 +0000 (17:48 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 28 Nov 2023 17:19:51 +0000 (17:19 +0000)
[ Upstream commit 73bde5a3294853947252cd9092a3517c7cb0cd2d ]

As I was working on a syzbot report, I found that KCSAN would
probably complain that reading q->head or q->tail without
barriers could lead to invalid results.

Add corresponding READ_ONCE() and WRITE_ONCE() to avoid
load-store tearing.

Fixes: d94ba80ebbea ("ptp: Added a brand new class driver for ptp clocks.")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Acked-by: Richard Cochran <richardcochran@gmail.com>
Link: https://lore.kernel.org/r/20231109174859.3995880-1-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/ptp/ptp_chardev.c
drivers/ptp/ptp_clock.c
drivers/ptp/ptp_private.h
drivers/ptp/ptp_sysfs.c

index 362bf75..5a3a4cc 100644 (file)
@@ -490,7 +490,8 @@ ssize_t ptp_read(struct posix_clock *pc,
 
        for (i = 0; i < cnt; i++) {
                event[i] = queue->buf[queue->head];
-               queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS;
+               /* Paired with READ_ONCE() in queue_cnt() */
+               WRITE_ONCE(queue->head, (queue->head + 1) % PTP_MAX_TIMESTAMPS);
        }
 
        spin_unlock_irqrestore(&queue->lock, flags);
index 80f74e3..9a50bfb 100644 (file)
@@ -56,10 +56,11 @@ static void enqueue_external_timestamp(struct timestamp_event_queue *queue,
        dst->t.sec = seconds;
        dst->t.nsec = remainder;
 
+       /* Both WRITE_ONCE() are paired with READ_ONCE() in queue_cnt() */
        if (!queue_free(queue))
-               queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS;
+               WRITE_ONCE(queue->head, (queue->head + 1) % PTP_MAX_TIMESTAMPS);
 
-       queue->tail = (queue->tail + 1) % PTP_MAX_TIMESTAMPS;
+       WRITE_ONCE(queue->tail, (queue->tail + 1) % PTP_MAX_TIMESTAMPS);
 
        spin_unlock_irqrestore(&queue->lock, flags);
 }
index 75f58fc..b8d4f61 100644 (file)
@@ -76,9 +76,13 @@ struct ptp_vclock {
  * that a writer might concurrently increment the tail does not
  * matter, since the queue remains nonempty nonetheless.
  */
-static inline int queue_cnt(struct timestamp_event_queue *q)
+static inline int queue_cnt(const struct timestamp_event_queue *q)
 {
-       int cnt = q->tail - q->head;
+       /*
+        * Paired with WRITE_ONCE() in enqueue_external_timestamp(),
+        * ptp_read(), extts_fifo_show().
+        */
+       int cnt = READ_ONCE(q->tail) - READ_ONCE(q->head);
        return cnt < 0 ? PTP_MAX_TIMESTAMPS + cnt : cnt;
 }
 
index 6e4d545..34ea5c1 100644 (file)
@@ -90,7 +90,8 @@ static ssize_t extts_fifo_show(struct device *dev,
        qcnt = queue_cnt(queue);
        if (qcnt) {
                event = queue->buf[queue->head];
-               queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS;
+               /* Paired with READ_ONCE() in queue_cnt() */
+               WRITE_ONCE(queue->head, (queue->head + 1) % PTP_MAX_TIMESTAMPS);
        }
        spin_unlock_irqrestore(&queue->lock, flags);