Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

media: dvb-core: Fix use-after-free due to race condition at dvb_ca_en50221

If the device node of dvb_ca_en50221 is open() and the
device is disconnected, a UAF may occur when calling
close() on the device node.

The root cause is that wake_up() and wait_event() for
dvbdev->wait_queue are not implemented.

So implement wait_event() function in dvb_ca_en50221_release()
and add 'remove_mutex' which prevents race condition
for 'ca->exit'.

[mchehab: fix a checkpatch warning]

Link: https://lore.kernel.org/linux-media/20221121063308.GA33821@ubuntu
Signed-off-by: Hyunwoo Kim <v4bel@theori.io>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>

authored by

Hyunwoo Kim and committed by
Mauro Carvalho Chehab
280a8ab8 b8c75e4a

+36 -1
+36 -1
drivers/media/dvb-core/dvb_ca_en50221.c
··· 151 151 152 152 /* mutex serializing ioctls */ 153 153 struct mutex ioctl_mutex; 154 + 155 + /* A mutex used when a device is disconnected */ 156 + struct mutex remove_mutex; 157 + 158 + /* Whether the device is disconnected */ 159 + int exit; 154 160 }; 155 161 156 162 static void dvb_ca_private_free(struct dvb_ca_private *ca) ··· 1717 1711 1718 1712 dprintk("%s\n", __func__); 1719 1713 1720 - if (!try_module_get(ca->pub->owner)) 1714 + mutex_lock(&ca->remove_mutex); 1715 + 1716 + if (ca->exit) { 1717 + mutex_unlock(&ca->remove_mutex); 1718 + return -ENODEV; 1719 + } 1720 + 1721 + if (!try_module_get(ca->pub->owner)) { 1722 + mutex_unlock(&ca->remove_mutex); 1721 1723 return -EIO; 1724 + } 1722 1725 1723 1726 err = dvb_generic_open(inode, file); 1724 1727 if (err < 0) { 1725 1728 module_put(ca->pub->owner); 1729 + mutex_unlock(&ca->remove_mutex); 1726 1730 return err; 1727 1731 } 1728 1732 ··· 1757 1741 1758 1742 dvb_ca_private_get(ca); 1759 1743 1744 + mutex_unlock(&ca->remove_mutex); 1760 1745 return 0; 1761 1746 } 1762 1747 ··· 1777 1760 1778 1761 dprintk("%s\n", __func__); 1779 1762 1763 + mutex_lock(&ca->remove_mutex); 1764 + 1780 1765 /* mark the CA device as closed */ 1781 1766 ca->open = 0; 1782 1767 dvb_ca_en50221_thread_update_delay(ca); ··· 1788 1769 module_put(ca->pub->owner); 1789 1770 1790 1771 dvb_ca_private_put(ca); 1772 + 1773 + if (dvbdev->users == 1 && ca->exit == 1) { 1774 + mutex_unlock(&ca->remove_mutex); 1775 + wake_up(&dvbdev->wait_queue); 1776 + } else { 1777 + mutex_unlock(&ca->remove_mutex); 1778 + } 1791 1779 1792 1780 return err; 1793 1781 } ··· 1919 1893 } 1920 1894 1921 1895 mutex_init(&ca->ioctl_mutex); 1896 + mutex_init(&ca->remove_mutex); 1922 1897 1923 1898 if (signal_pending(current)) { 1924 1899 ret = -EINTR; ··· 1961 1934 int i; 1962 1935 1963 1936 dprintk("%s\n", __func__); 1937 + 1938 + mutex_lock(&ca->remove_mutex); 1939 + ca->exit = 1; 1940 + mutex_unlock(&ca->remove_mutex); 1941 + 1942 + if (ca->dvbdev->users < 1) 1943 + wait_event(ca->dvbdev->wait_queue, 1944 + ca->dvbdev->users == 1); 1964 1945 1965 1946 /* shutdown the thread if there was one */ 1966 1947 kthread_stop(ca->thread);