Re: [PATCH] module: always complete idempotent loads

From: Linus Torvalds
Date: Tue Jul 04 2023 - 09:38:13 EST


On Tue, 4 Jul 2023 at 03:09, Vegard Nossum <vegard.nossum@xxxxxxxxxx> wrote:
>
> Commit 9b9879fc0327 added a hashtable storing lists of concurrent module
> loads. However, it didn't fix up all the error paths in
> init_module_from_file(); this would lead to leaving the function while an
> on-stack 'struct idempotent' element is still in the hash table, which
> leads to all sorts of badness as spotted by syzkaller:

You are of course 100% right.

However, I'd rather just use a wrapper function and make this thing
much clearer. Like I should have done originally.

So I'd be inclined towards a patch like the attached instead. Works for you?

Linus
kernel/module/main.c | 32 ++++++++++++++++++++------------
1 file changed, 20 insertions(+), 12 deletions(-)

diff --git a/kernel/module/main.c b/kernel/module/main.c
index 834de86ebe35..500a6a3c2987 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -3092,7 +3092,7 @@ static bool idempotent(struct idempotent *u, const void *cookie)
* remove everybody - which includes ourselves - fill in the return
* value, and then complete the operation.
*/
-static void idempotent_complete(struct idempotent *u, int ret)
+static int idempotent_complete(struct idempotent *u, int ret)
{
const void *cookie = u->cookie;
int hash = hash_ptr(cookie, IDEM_HASH_BITS);
@@ -3109,23 +3109,18 @@ static void idempotent_complete(struct idempotent *u, int ret)
complete(&pos->complete);
}
spin_unlock(&idem_lock);
+ return ret;
}

static int init_module_from_file(struct file *f, const char __user * uargs, int flags)
{
- struct idempotent idem;
struct load_info info = { };
void *buf = NULL;
- int len, ret;
+ int len;

if (!f || !(f->f_mode & FMODE_READ))
return -EBADF;

- if (idempotent(&idem, file_inode(f))) {
- wait_for_completion(&idem.complete);
- return idem.ret;
- }
-
len = kernel_read_file(f, 0, &buf, INT_MAX, NULL, READING_MODULE);
if (len < 0) {
mod_stat_inc(&failed_kreads);
@@ -3146,9 +3141,22 @@ static int init_module_from_file(struct file *f, const char __user * uargs, int
info.len = len;
}

- ret = load_module(&info, uargs, flags);
- idempotent_complete(&idem, ret);
- return ret;
+ return load_module(&info, uargs, flags);
+}
+
+static int idempotent_init_module(struct file *f, const char __user * uargs, int flags)
+{
+ struct idempotent idem;
+
+ /* See if somebody else is doing the operation? */
+ if (idempotent(&idem, file_inode(f))) {
+ wait_for_completion(&idem.complete);
+ return idem.ret;
+ }
+
+ /* Otherwise, we'll do it and complete others */
+ return idempotent_complete(&idem,
+ init_module_from_file(f, uargs, flags));
}

SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags)
@@ -3168,7 +3176,7 @@ SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags)
return -EINVAL;

f = fdget(fd);
- err = init_module_from_file(f.file, uargs, flags);
+ err = idempotent_init_module(f.file, uargs, flags);
fdput(f);
return err;
}