Skip to content

Commit 6769a0b

Browse files
V4belmchehab
authored andcommitted
media: dvb-core: Fix use-after-free on race condition at dvb_frontend
If the device node of dvb_frontend is open() and the device is disconnected, many kinds of UAFs may occur when calling close() on the device node. The root cause of this is that wake_up() for dvbdev->wait_queue is implemented in the dvb_frontend_release() function, but wait_event() is not implemented in the dvb_frontend_stop() function. So, implement wait_event() function in dvb_frontend_stop() and add 'remove_mutex' which prevents race condition for 'fe->exit'. [mchehab: fix a couple of checkpatch warnings and some mistakes at the error handling logic] Link: https://lore.kernel.org/linux-media/[email protected] Signed-off-by: Hyunwoo Kim <[email protected]> Signed-off-by: Mauro Carvalho Chehab <[email protected]>
1 parent ae11c0e commit 6769a0b

File tree

2 files changed

+49
-10
lines changed

2 files changed

+49
-10
lines changed

drivers/media/dvb-core/dvb_frontend.c

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -809,15 +809,26 @@ static void dvb_frontend_stop(struct dvb_frontend *fe)
809809

810810
dev_dbg(fe->dvb->device, "%s:\n", __func__);
811811

812+
mutex_lock(&fe->remove_mutex);
813+
812814
if (fe->exit != DVB_FE_DEVICE_REMOVED)
813815
fe->exit = DVB_FE_NORMAL_EXIT;
814816
mb();
815817

816-
if (!fepriv->thread)
818+
if (!fepriv->thread) {
819+
mutex_unlock(&fe->remove_mutex);
817820
return;
821+
}
818822

819823
kthread_stop(fepriv->thread);
820824

825+
mutex_unlock(&fe->remove_mutex);
826+
827+
if (fepriv->dvbdev->users < -1) {
828+
wait_event(fepriv->dvbdev->wait_queue,
829+
fepriv->dvbdev->users == -1);
830+
}
831+
821832
sema_init(&fepriv->sem, 1);
822833
fepriv->state = FESTATE_IDLE;
823834

@@ -2761,17 +2772,22 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
27612772
struct dvb_adapter *adapter = fe->dvb;
27622773
int ret;
27632774

2775+
mutex_lock(&fe->remove_mutex);
2776+
27642777
dev_dbg(fe->dvb->device, "%s:\n", __func__);
2765-
if (fe->exit == DVB_FE_DEVICE_REMOVED)
2766-
return -ENODEV;
2778+
if (fe->exit == DVB_FE_DEVICE_REMOVED) {
2779+
ret = -ENODEV;
2780+
goto err_remove_mutex;
2781+
}
27672782

27682783
if (adapter->mfe_shared == 2) {
27692784
mutex_lock(&adapter->mfe_lock);
27702785
if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
27712786
if (adapter->mfe_dvbdev &&
27722787
!adapter->mfe_dvbdev->writers) {
27732788
mutex_unlock(&adapter->mfe_lock);
2774-
return -EBUSY;
2789+
ret = -EBUSY;
2790+
goto err_remove_mutex;
27752791
}
27762792
adapter->mfe_dvbdev = dvbdev;
27772793
}
@@ -2794,8 +2810,10 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
27942810
while (mferetry-- && (mfedev->users != -1 ||
27952811
mfepriv->thread)) {
27962812
if (msleep_interruptible(500)) {
2797-
if (signal_pending(current))
2798-
return -EINTR;
2813+
if (signal_pending(current)) {
2814+
ret = -EINTR;
2815+
goto err_remove_mutex;
2816+
}
27992817
}
28002818
}
28012819

@@ -2807,7 +2825,8 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
28072825
if (mfedev->users != -1 ||
28082826
mfepriv->thread) {
28092827
mutex_unlock(&adapter->mfe_lock);
2810-
return -EBUSY;
2828+
ret = -EBUSY;
2829+
goto err_remove_mutex;
28112830
}
28122831
adapter->mfe_dvbdev = dvbdev;
28132832
}
@@ -2866,6 +2885,8 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
28662885

28672886
if (adapter->mfe_shared)
28682887
mutex_unlock(&adapter->mfe_lock);
2888+
2889+
mutex_unlock(&fe->remove_mutex);
28692890
return ret;
28702891

28712892
err3:
@@ -2887,6 +2908,9 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
28872908
err0:
28882909
if (adapter->mfe_shared)
28892910
mutex_unlock(&adapter->mfe_lock);
2911+
2912+
err_remove_mutex:
2913+
mutex_unlock(&fe->remove_mutex);
28902914
return ret;
28912915
}
28922916

@@ -2897,6 +2921,8 @@ static int dvb_frontend_release(struct inode *inode, struct file *file)
28972921
struct dvb_frontend_private *fepriv = fe->frontend_priv;
28982922
int ret;
28992923

2924+
mutex_lock(&fe->remove_mutex);
2925+
29002926
dev_dbg(fe->dvb->device, "%s:\n", __func__);
29012927

29022928
if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
@@ -2918,10 +2944,18 @@ static int dvb_frontend_release(struct inode *inode, struct file *file)
29182944
}
29192945
mutex_unlock(&fe->dvb->mdev_lock);
29202946
#endif
2921-
if (fe->exit != DVB_FE_NO_EXIT)
2922-
wake_up(&dvbdev->wait_queue);
29232947
if (fe->ops.ts_bus_ctrl)
29242948
fe->ops.ts_bus_ctrl(fe, 0);
2949+
2950+
if (fe->exit != DVB_FE_NO_EXIT) {
2951+
mutex_unlock(&fe->remove_mutex);
2952+
wake_up(&dvbdev->wait_queue);
2953+
} else {
2954+
mutex_unlock(&fe->remove_mutex);
2955+
}
2956+
2957+
} else {
2958+
mutex_unlock(&fe->remove_mutex);
29252959
}
29262960

29272961
dvb_frontend_put(fe);
@@ -3022,6 +3056,7 @@ int dvb_register_frontend(struct dvb_adapter *dvb,
30223056
fepriv = fe->frontend_priv;
30233057

30243058
kref_init(&fe->refcount);
3059+
mutex_init(&fe->remove_mutex);
30253060

30263061
/*
30273062
* After initialization, there need to be two references: one

include/media/dvb_frontend.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,10 @@ struct dtv_frontend_properties {
686686
* @id: Frontend ID
687687
* @exit: Used to inform the DVB core that the frontend
688688
* thread should exit (usually, means that the hardware
689-
* got disconnected.
689+
* got disconnected).
690+
* @remove_mutex: mutex that avoids a race condition between a callback
691+
* called when the hardware is disconnected and the
692+
* file_operations of dvb_frontend.
690693
*/
691694

692695
struct dvb_frontend {
@@ -704,6 +707,7 @@ struct dvb_frontend {
704707
int (*callback)(void *adapter_priv, int component, int cmd, int arg);
705708
int id;
706709
unsigned int exit;
710+
struct mutex remove_mutex;
707711
};
708712

709713
/**

0 commit comments

Comments
 (0)