mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-08 15:04:45 +00:00
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:
parent
b714ca2ccf
commit
1bea2c3e6d
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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 */
|
||||||
|
Loading…
Reference in New Issue
Block a user