ptp: fix corrupted list in ptp_open
authorEdward Adam Davis <eadavis@qq.com>
Tue, 7 Nov 2023 08:00:41 +0000 (16:00 +0800)
committerJakub Kicinski <kuba@kernel.org>
Thu, 9 Nov 2023 02:47:07 +0000 (18:47 -0800)
There is no lock protection when writing ptp->tsevqs in ptp_open() and
ptp_release(), which can cause data corruption, use spin lock to avoid this
issue.

Moreover, ptp_release() should not be used to release the queue in ptp_read(),
and it should be deleted altogether.

Acked-by: Richard Cochran <richardcochran@gmail.com>
Reported-and-tested-by: syzbot+df3f3ef31f60781fa911@syzkaller.appspotmail.com
Fixes: 8f5de6fb2453 ("ptp: support multiple timestamp event readers")
Signed-off-by: Edward Adam Davis <eadavis@qq.com>
Link: https://lore.kernel.org/r/tencent_CD19564FFE8DA8A5918DFE92325D92DD8107@qq.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/ptp/ptp_chardev.c
drivers/ptp/ptp_clock.c
drivers/ptp/ptp_private.h

index 27c1ef4936174491daa716e39c5ede2de13d8c35..3f7a7478880240a2d256caf624b61dcc8e7054af 100644 (file)
@@ -108,6 +108,7 @@ int ptp_open(struct posix_clock_context *pccontext, fmode_t fmode)
                container_of(pccontext->clk, struct ptp_clock, clock);
        struct timestamp_event_queue *queue;
        char debugfsname[32];
+       unsigned long flags;
 
        queue = kzalloc(sizeof(*queue), GFP_KERNEL);
        if (!queue)
@@ -119,7 +120,9 @@ int ptp_open(struct posix_clock_context *pccontext, fmode_t fmode)
        }
        bitmap_set(queue->mask, 0, PTP_MAX_CHANNELS);
        spin_lock_init(&queue->lock);
+       spin_lock_irqsave(&ptp->tsevqs_lock, flags);
        list_add_tail(&queue->qlist, &ptp->tsevqs);
+       spin_unlock_irqrestore(&ptp->tsevqs_lock, flags);
        pccontext->private_clkdata = queue;
 
        /* Debugfs contents */
@@ -139,16 +142,16 @@ int ptp_release(struct posix_clock_context *pccontext)
 {
        struct timestamp_event_queue *queue = pccontext->private_clkdata;
        unsigned long flags;
+       struct ptp_clock *ptp =
+               container_of(pccontext->clk, struct ptp_clock, clock);
 
-       if (queue) {
-               debugfs_remove(queue->debugfs_instance);
-               pccontext->private_clkdata = NULL;
-               spin_lock_irqsave(&queue->lock, flags);
-               list_del(&queue->qlist);
-               spin_unlock_irqrestore(&queue->lock, flags);
-               bitmap_free(queue->mask);
-               kfree(queue);
-       }
+       debugfs_remove(queue->debugfs_instance);
+       pccontext->private_clkdata = NULL;
+       spin_lock_irqsave(&ptp->tsevqs_lock, flags);
+       list_del(&queue->qlist);
+       spin_unlock_irqrestore(&ptp->tsevqs_lock, flags);
+       bitmap_free(queue->mask);
+       kfree(queue);
        return 0;
 }
 
index 3d1b0a97301c95cc9daf9d298a8b85974af1cda5..3134568af622d396f6ab15049cd1a3ace3243269 100644 (file)
@@ -179,11 +179,11 @@ static void ptp_clock_release(struct device *dev)
        mutex_destroy(&ptp->pincfg_mux);
        mutex_destroy(&ptp->n_vclocks_mux);
        /* Delete first entry */
+       spin_lock_irqsave(&ptp->tsevqs_lock, flags);
        tsevq = list_first_entry(&ptp->tsevqs, struct timestamp_event_queue,
                                 qlist);
-       spin_lock_irqsave(&tsevq->lock, flags);
        list_del(&tsevq->qlist);
-       spin_unlock_irqrestore(&tsevq->lock, flags);
+       spin_unlock_irqrestore(&ptp->tsevqs_lock, flags);
        bitmap_free(tsevq->mask);
        kfree(tsevq);
        debugfs_remove(ptp->debugfs_root);
@@ -247,6 +247,7 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
        if (!queue)
                goto no_memory_queue;
        list_add_tail(&queue->qlist, &ptp->tsevqs);
+       spin_lock_init(&ptp->tsevqs_lock);
        queue->mask = bitmap_alloc(PTP_MAX_CHANNELS, GFP_KERNEL);
        if (!queue->mask)
                goto no_memory_bitmap;
@@ -407,6 +408,7 @@ void ptp_clock_event(struct ptp_clock *ptp, struct ptp_clock_event *event)
 {
        struct timestamp_event_queue *tsevq;
        struct pps_event_time evt;
+       unsigned long flags;
 
        switch (event->type) {
 
@@ -415,10 +417,12 @@ void ptp_clock_event(struct ptp_clock *ptp, struct ptp_clock_event *event)
 
        case PTP_CLOCK_EXTTS:
                /* Enqueue timestamp on selected queues */
+               spin_lock_irqsave(&ptp->tsevqs_lock, flags);
                list_for_each_entry(tsevq, &ptp->tsevqs, qlist) {
                        if (test_bit((unsigned int)event->index, tsevq->mask))
                                enqueue_external_timestamp(tsevq, event);
                }
+               spin_unlock_irqrestore(&ptp->tsevqs_lock, flags);
                wake_up_interruptible(&ptp->tsev_wq);
                break;
 
index 52f87e394aa641ffb434fa98fd7c39b6a8d1fd07..35fde0a0574606a04d6bdf0ab42a204da5fa6532 100644 (file)
@@ -44,6 +44,7 @@ struct ptp_clock {
        struct pps_device *pps_source;
        long dialed_frequency; /* remembers the frequency adjustment */
        struct list_head tsevqs; /* timestamp fifo list */
+       spinlock_t tsevqs_lock; /* protects tsevqs from concurrent access */
        struct mutex pincfg_mux; /* protect concurrent info->pin_config access */
        wait_queue_head_t tsev_wq;
        int defunct; /* tells readers to go away when clock is being removed */