[PATCH] char: misc: make misc_open() and misc_register() killable

Tetsuo Handa
2022-07-04T06:44:21Z

syzbot is reporting hung task at misc_open() [1], for snapshot_open() from
misc_open() might sleep for long with misc_mtx held whereas userspace can
flood with concurrent misc_open() requests. Mitigate this problem by making
misc_open() and misc_register() killable.

Link: https://syzkaller.appspot.com/bug?extid=358c9ab4c93da7b7238c [1]
Reported-by: syzbot <syzbot+358c9ab4c93da7b7238c@syzkaller.appspotmail.com>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
drivers/char/misc.c | 57 +++++++++++++++++++--------------------------
1 file changed, 24 insertions(+), 33 deletions(-)

diff --git a/drivers/char/misc.c b/drivers/char/misc.c
index cba19bfdc44d..b9a494bc4228 100644
--- a/drivers/char/misc.c
+++ b/drivers/char/misc.c
@@ -100,49 +100,39 @@ static const struct seq_operations misc_seq_ops = {
static int misc_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
- struct miscdevice *c = NULL, *iter;
+ struct miscdevice *iter;
int err = -ENODEV;
const struct file_operations *new_fops = NULL;
+ bool retried = false;

- mutex_lock(&misc_mtx);
-
+ retry:
+ if (mutex_lock_killable(&misc_mtx))
+ return -EINTR;
list_for_each_entry(iter, &misc_list, list) {
if (iter->minor != minor)
continue;
- c = iter;
new_fops = fops_get(iter->fops);
+ if (!new_fops)
+ break;
+ /*
+ * Place the miscdevice in the file's
+ * private_data so it can be used by the
+ * file operations, including f_op->open below
+ */
+ file->private_data = iter;
+
+ err = 0;
+ replace_fops(file, new_fops);
+ if (file->f_op->open)
+ err = file->f_op->open(inode, file);
break;
}
-
- if (!new_fops) {
- mutex_unlock(&misc_mtx);
+ mutex_unlock(&misc_mtx);
+ if (!new_fops && !retried) {
request_module("char-major-%d-%d", MISC_MAJOR, minor);
- mutex_lock(&misc_mtx);
-
- list_for_each_entry(iter, &misc_list, list) {
- if (iter->minor != minor)
- continue;
- c = iter;
- new_fops = fops_get(iter->fops);
- break;
- }
- if (!new_fops)
- goto fail;
+ retried = true;
+ goto retry;
}
-
- /*
- * Place the miscdevice in the file's
- * private_data so it can be used by the
- * file operations, including f_op->open below
- */
- file->private_data = c;
-
- err = 0;
- replace_fops(file, new_fops);
- if (file->f_op->open)
- err = file->f_op->open(inode, file);
-fail:
- mutex_unlock(&misc_mtx);
return err;
}

@@ -180,7 +170,8 @@ int misc_register(struct miscdevice *misc)

INIT_LIST_HEAD(&misc->list);

- mutex_lock(&misc_mtx);
+ if (mutex_lock_killable(&misc_mtx))
+ return -EINTR;

if (is_dynamic) {
int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
--
2.18.4