Skip to content

Commit 3424c8f

Browse files
committed
ALSA: timer: Don't take register_mutex with copy_from/to_user()
The infamous mmap_lock taken in copy_from/to_user() can be often problematic when it's called inside another mutex, as they might lead to deadlocks. In the case of ALSA timer code, the bad pattern is with guard(mutex)(&register_mutex) that covers copy_from/to_user() -- which was mistakenly introduced at converting to guard(), and it had been carefully worked around in the past. This patch fixes those pieces simply by moving copy_from/to_user() out of the register mutex lock again. Fixes: 3923de0 ("ALSA: pcm: oss: Use guard() for setup") Reported-by: [email protected] Closes: https://lore.kernel.org/[email protected] Link: https://patch.msgid.link/[email protected] Signed-off-by: Takashi Iwai <[email protected]>
1 parent e8e472d commit 3424c8f

File tree

1 file changed

+77
-70
lines changed

1 file changed

+77
-70
lines changed

sound/core/timer.c

Lines changed: 77 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1515,91 +1515,97 @@ static void snd_timer_user_copy_id(struct snd_timer_id *id, struct snd_timer *ti
15151515
id->subdevice = timer->tmr_subdevice;
15161516
}
15171517

1518-
static int snd_timer_user_next_device(struct snd_timer_id __user *_tid)
1518+
static void get_next_device(struct snd_timer_id *id)
15191519
{
1520-
struct snd_timer_id id;
15211520
struct snd_timer *timer;
15221521
struct list_head *p;
15231522

1524-
if (copy_from_user(&id, _tid, sizeof(id)))
1525-
return -EFAULT;
1526-
guard(mutex)(&register_mutex);
1527-
if (id.dev_class < 0) { /* first item */
1523+
if (id->dev_class < 0) { /* first item */
15281524
if (list_empty(&snd_timer_list))
1529-
snd_timer_user_zero_id(&id);
1525+
snd_timer_user_zero_id(id);
15301526
else {
15311527
timer = list_entry(snd_timer_list.next,
15321528
struct snd_timer, device_list);
1533-
snd_timer_user_copy_id(&id, timer);
1529+
snd_timer_user_copy_id(id, timer);
15341530
}
15351531
} else {
1536-
switch (id.dev_class) {
1532+
switch (id->dev_class) {
15371533
case SNDRV_TIMER_CLASS_GLOBAL:
1538-
id.device = id.device < 0 ? 0 : id.device + 1;
1534+
id->device = id->device < 0 ? 0 : id->device + 1;
15391535
list_for_each(p, &snd_timer_list) {
15401536
timer = list_entry(p, struct snd_timer, device_list);
15411537
if (timer->tmr_class > SNDRV_TIMER_CLASS_GLOBAL) {
1542-
snd_timer_user_copy_id(&id, timer);
1538+
snd_timer_user_copy_id(id, timer);
15431539
break;
15441540
}
1545-
if (timer->tmr_device >= id.device) {
1546-
snd_timer_user_copy_id(&id, timer);
1541+
if (timer->tmr_device >= id->device) {
1542+
snd_timer_user_copy_id(id, timer);
15471543
break;
15481544
}
15491545
}
15501546
if (p == &snd_timer_list)
1551-
snd_timer_user_zero_id(&id);
1547+
snd_timer_user_zero_id(id);
15521548
break;
15531549
case SNDRV_TIMER_CLASS_CARD:
15541550
case SNDRV_TIMER_CLASS_PCM:
1555-
if (id.card < 0) {
1556-
id.card = 0;
1551+
if (id->card < 0) {
1552+
id->card = 0;
15571553
} else {
1558-
if (id.device < 0) {
1559-
id.device = 0;
1554+
if (id->device < 0) {
1555+
id->device = 0;
15601556
} else {
1561-
if (id.subdevice < 0)
1562-
id.subdevice = 0;
1563-
else if (id.subdevice < INT_MAX)
1564-
id.subdevice++;
1557+
if (id->subdevice < 0)
1558+
id->subdevice = 0;
1559+
else if (id->subdevice < INT_MAX)
1560+
id->subdevice++;
15651561
}
15661562
}
15671563
list_for_each(p, &snd_timer_list) {
15681564
timer = list_entry(p, struct snd_timer, device_list);
1569-
if (timer->tmr_class > id.dev_class) {
1570-
snd_timer_user_copy_id(&id, timer);
1565+
if (timer->tmr_class > id->dev_class) {
1566+
snd_timer_user_copy_id(id, timer);
15711567
break;
15721568
}
1573-
if (timer->tmr_class < id.dev_class)
1569+
if (timer->tmr_class < id->dev_class)
15741570
continue;
1575-
if (timer->card->number > id.card) {
1576-
snd_timer_user_copy_id(&id, timer);
1571+
if (timer->card->number > id->card) {
1572+
snd_timer_user_copy_id(id, timer);
15771573
break;
15781574
}
1579-
if (timer->card->number < id.card)
1575+
if (timer->card->number < id->card)
15801576
continue;
1581-
if (timer->tmr_device > id.device) {
1582-
snd_timer_user_copy_id(&id, timer);
1577+
if (timer->tmr_device > id->device) {
1578+
snd_timer_user_copy_id(id, timer);
15831579
break;
15841580
}
1585-
if (timer->tmr_device < id.device)
1581+
if (timer->tmr_device < id->device)
15861582
continue;
1587-
if (timer->tmr_subdevice > id.subdevice) {
1588-
snd_timer_user_copy_id(&id, timer);
1583+
if (timer->tmr_subdevice > id->subdevice) {
1584+
snd_timer_user_copy_id(id, timer);
15891585
break;
15901586
}
1591-
if (timer->tmr_subdevice < id.subdevice)
1587+
if (timer->tmr_subdevice < id->subdevice)
15921588
continue;
1593-
snd_timer_user_copy_id(&id, timer);
1589+
snd_timer_user_copy_id(id, timer);
15941590
break;
15951591
}
15961592
if (p == &snd_timer_list)
1597-
snd_timer_user_zero_id(&id);
1593+
snd_timer_user_zero_id(id);
15981594
break;
15991595
default:
1600-
snd_timer_user_zero_id(&id);
1596+
snd_timer_user_zero_id(id);
16011597
}
16021598
}
1599+
}
1600+
1601+
static int snd_timer_user_next_device(struct snd_timer_id __user *_tid)
1602+
{
1603+
struct snd_timer_id id;
1604+
1605+
if (copy_from_user(&id, _tid, sizeof(id)))
1606+
return -EFAULT;
1607+
scoped_guard(mutex, &register_mutex)
1608+
get_next_device(&id);
16031609
if (copy_to_user(_tid, &id, sizeof(*_tid)))
16041610
return -EFAULT;
16051611
return 0;
@@ -1620,23 +1626,24 @@ static int snd_timer_user_ginfo(struct file *file,
16201626
tid = ginfo->tid;
16211627
memset(ginfo, 0, sizeof(*ginfo));
16221628
ginfo->tid = tid;
1623-
guard(mutex)(&register_mutex);
1624-
t = snd_timer_find(&tid);
1625-
if (!t)
1626-
return -ENODEV;
1627-
ginfo->card = t->card ? t->card->number : -1;
1628-
if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
1629-
ginfo->flags |= SNDRV_TIMER_FLG_SLAVE;
1630-
strscpy(ginfo->id, t->id, sizeof(ginfo->id));
1631-
strscpy(ginfo->name, t->name, sizeof(ginfo->name));
1632-
scoped_guard(spinlock_irq, &t->lock)
1633-
ginfo->resolution = snd_timer_hw_resolution(t);
1634-
if (t->hw.resolution_min > 0) {
1635-
ginfo->resolution_min = t->hw.resolution_min;
1636-
ginfo->resolution_max = t->hw.resolution_max;
1637-
}
1638-
list_for_each(p, &t->open_list_head) {
1639-
ginfo->clients++;
1629+
scoped_guard(mutex, &register_mutex) {
1630+
t = snd_timer_find(&tid);
1631+
if (!t)
1632+
return -ENODEV;
1633+
ginfo->card = t->card ? t->card->number : -1;
1634+
if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
1635+
ginfo->flags |= SNDRV_TIMER_FLG_SLAVE;
1636+
strscpy(ginfo->id, t->id, sizeof(ginfo->id));
1637+
strscpy(ginfo->name, t->name, sizeof(ginfo->name));
1638+
scoped_guard(spinlock_irq, &t->lock)
1639+
ginfo->resolution = snd_timer_hw_resolution(t);
1640+
if (t->hw.resolution_min > 0) {
1641+
ginfo->resolution_min = t->hw.resolution_min;
1642+
ginfo->resolution_max = t->hw.resolution_max;
1643+
}
1644+
list_for_each(p, &t->open_list_head) {
1645+
ginfo->clients++;
1646+
}
16401647
}
16411648
if (copy_to_user(_ginfo, ginfo, sizeof(*ginfo)))
16421649
return -EFAULT;
@@ -1674,31 +1681,31 @@ static int snd_timer_user_gstatus(struct file *file,
16741681
struct snd_timer_gstatus gstatus;
16751682
struct snd_timer_id tid;
16761683
struct snd_timer *t;
1677-
int err = 0;
16781684

16791685
if (copy_from_user(&gstatus, _gstatus, sizeof(gstatus)))
16801686
return -EFAULT;
16811687
tid = gstatus.tid;
16821688
memset(&gstatus, 0, sizeof(gstatus));
16831689
gstatus.tid = tid;
1684-
guard(mutex)(&register_mutex);
1685-
t = snd_timer_find(&tid);
1686-
if (t != NULL) {
1687-
guard(spinlock_irq)(&t->lock);
1688-
gstatus.resolution = snd_timer_hw_resolution(t);
1689-
if (t->hw.precise_resolution) {
1690-
t->hw.precise_resolution(t, &gstatus.resolution_num,
1691-
&gstatus.resolution_den);
1690+
scoped_guard(mutex, &register_mutex) {
1691+
t = snd_timer_find(&tid);
1692+
if (t != NULL) {
1693+
guard(spinlock_irq)(&t->lock);
1694+
gstatus.resolution = snd_timer_hw_resolution(t);
1695+
if (t->hw.precise_resolution) {
1696+
t->hw.precise_resolution(t, &gstatus.resolution_num,
1697+
&gstatus.resolution_den);
1698+
} else {
1699+
gstatus.resolution_num = gstatus.resolution;
1700+
gstatus.resolution_den = 1000000000uL;
1701+
}
16921702
} else {
1693-
gstatus.resolution_num = gstatus.resolution;
1694-
gstatus.resolution_den = 1000000000uL;
1703+
return -ENODEV;
16951704
}
1696-
} else {
1697-
err = -ENODEV;
16981705
}
1699-
if (err >= 0 && copy_to_user(_gstatus, &gstatus, sizeof(gstatus)))
1700-
err = -EFAULT;
1701-
return err;
1706+
if (copy_to_user(_gstatus, &gstatus, sizeof(gstatus)))
1707+
return -EFAULT;
1708+
return 0;
17021709
}
17031710

17041711
static int snd_timer_user_tselect(struct file *file,

0 commit comments

Comments
 (0)