ptp: fix corrupted list in ptp_open

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: 8f5de6fb24 ("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>
This commit is contained in:
Edward Adam Davis 2023-11-07 16:00:41 +08:00 committed by Jakub Kicinski
parent b714ca2ccf
commit 1bea2c3e6d
3 changed files with 19 additions and 11 deletions

View File

@ -108,6 +108,7 @@ int ptp_open(struct posix_clock_context *pccontext, fmode_t fmode)
container_of(pccontext->clk, struct ptp_clock, clock); container_of(pccontext->clk, struct ptp_clock, clock);
struct timestamp_event_queue *queue; struct timestamp_event_queue *queue;
char debugfsname[32]; char debugfsname[32];
unsigned long flags;
queue = kzalloc(sizeof(*queue), GFP_KERNEL); queue = kzalloc(sizeof(*queue), GFP_KERNEL);
if (!queue) 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); bitmap_set(queue->mask, 0, PTP_MAX_CHANNELS);
spin_lock_init(&queue->lock); spin_lock_init(&queue->lock);
spin_lock_irqsave(&ptp->tsevqs_lock, flags);
list_add_tail(&queue->qlist, &ptp->tsevqs); list_add_tail(&queue->qlist, &ptp->tsevqs);
spin_unlock_irqrestore(&ptp->tsevqs_lock, flags);
pccontext->private_clkdata = queue; pccontext->private_clkdata = queue;
/* Debugfs contents */ /* Debugfs contents */
@ -139,16 +142,16 @@ int ptp_release(struct posix_clock_context *pccontext)
{ {
struct timestamp_event_queue *queue = pccontext->private_clkdata; struct timestamp_event_queue *queue = pccontext->private_clkdata;
unsigned long flags; unsigned long flags;
struct ptp_clock *ptp =
container_of(pccontext->clk, struct ptp_clock, clock);
if (queue) { debugfs_remove(queue->debugfs_instance);
debugfs_remove(queue->debugfs_instance); pccontext->private_clkdata = NULL;
pccontext->private_clkdata = NULL; spin_lock_irqsave(&ptp->tsevqs_lock, flags);
spin_lock_irqsave(&queue->lock, flags); list_del(&queue->qlist);
list_del(&queue->qlist); spin_unlock_irqrestore(&ptp->tsevqs_lock, flags);
spin_unlock_irqrestore(&queue->lock, flags); bitmap_free(queue->mask);
bitmap_free(queue->mask); kfree(queue);
kfree(queue);
}
return 0; return 0;
} }

View File

@ -179,11 +179,11 @@ static void ptp_clock_release(struct device *dev)
mutex_destroy(&ptp->pincfg_mux); mutex_destroy(&ptp->pincfg_mux);
mutex_destroy(&ptp->n_vclocks_mux); mutex_destroy(&ptp->n_vclocks_mux);
/* Delete first entry */ /* Delete first entry */
spin_lock_irqsave(&ptp->tsevqs_lock, flags);
tsevq = list_first_entry(&ptp->tsevqs, struct timestamp_event_queue, tsevq = list_first_entry(&ptp->tsevqs, struct timestamp_event_queue,
qlist); qlist);
spin_lock_irqsave(&tsevq->lock, flags);
list_del(&tsevq->qlist); list_del(&tsevq->qlist);
spin_unlock_irqrestore(&tsevq->lock, flags); spin_unlock_irqrestore(&ptp->tsevqs_lock, flags);
bitmap_free(tsevq->mask); bitmap_free(tsevq->mask);
kfree(tsevq); kfree(tsevq);
debugfs_remove(ptp->debugfs_root); debugfs_remove(ptp->debugfs_root);
@ -247,6 +247,7 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
if (!queue) if (!queue)
goto no_memory_queue; goto no_memory_queue;
list_add_tail(&queue->qlist, &ptp->tsevqs); list_add_tail(&queue->qlist, &ptp->tsevqs);
spin_lock_init(&ptp->tsevqs_lock);
queue->mask = bitmap_alloc(PTP_MAX_CHANNELS, GFP_KERNEL); queue->mask = bitmap_alloc(PTP_MAX_CHANNELS, GFP_KERNEL);
if (!queue->mask) if (!queue->mask)
goto no_memory_bitmap; 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 timestamp_event_queue *tsevq;
struct pps_event_time evt; struct pps_event_time evt;
unsigned long flags;
switch (event->type) { switch (event->type) {
@ -415,10 +417,12 @@ void ptp_clock_event(struct ptp_clock *ptp, struct ptp_clock_event *event)
case PTP_CLOCK_EXTTS: case PTP_CLOCK_EXTTS:
/* Enqueue timestamp on selected queues */ /* Enqueue timestamp on selected queues */
spin_lock_irqsave(&ptp->tsevqs_lock, flags);
list_for_each_entry(tsevq, &ptp->tsevqs, qlist) { list_for_each_entry(tsevq, &ptp->tsevqs, qlist) {
if (test_bit((unsigned int)event->index, tsevq->mask)) if (test_bit((unsigned int)event->index, tsevq->mask))
enqueue_external_timestamp(tsevq, event); enqueue_external_timestamp(tsevq, event);
} }
spin_unlock_irqrestore(&ptp->tsevqs_lock, flags);
wake_up_interruptible(&ptp->tsev_wq); wake_up_interruptible(&ptp->tsev_wq);
break; break;

View File

@ -44,6 +44,7 @@ struct ptp_clock {
struct pps_device *pps_source; struct pps_device *pps_source;
long dialed_frequency; /* remembers the frequency adjustment */ long dialed_frequency; /* remembers the frequency adjustment */
struct list_head tsevqs; /* timestamp fifo list */ 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 */ struct mutex pincfg_mux; /* protect concurrent info->pin_config access */
wait_queue_head_t tsev_wq; wait_queue_head_t tsev_wq;
int defunct; /* tells readers to go away when clock is being removed */ int defunct; /* tells readers to go away when clock is being removed */