* [PATCH] module: always complete idempotent loads
[not found] <20230704092309.22669-1-vegard.nossum@oracle.com>
@ 2023-07-04 10:08 ` Vegard Nossum
2023-07-04 13:37 ` Linus Torvalds
0 siblings, 1 reply; 6+ messages in thread
From: Vegard Nossum @ 2023-07-04 10:08 UTC (permalink / raw)
To: Luis Chamberlain
Cc: George Kennedy, linux-kernel, linux-modules, Vegard Nossum,
Harshit Mogalapalli, syzbot+9c2bdc9d24e4a7abe741, Johan Hovold,
Dan Williams, Rudi Heitbaum, David Hildenbrand, Linus Torvalds
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:
BUG: soft lockup in sys_finit_module
BUG: unable to handle kernel paging request in init_module_from_file
general protection fault in init_module_from_file
INFO: task hung in init_module_from_file
KASAN: out-of-bounds Read in init_module_from_file
KASAN: slab-out-of-bounds Read in init_module_from_file
KASAN: slab-use-after-free Read in init_module_from_file
KASAN: slab-use-after-free Read in set_powered_sync
KASAN: stack-out-of-bounds Read in init_module_from_file
KASAN: use-after-free in init_module_from_file
KASAN: use-after-free Read in init_module_from_file
Fixes: 9b9879fc0327 ("modules: catch concurrent module loads, treat them as idempotent")
Reported-by: Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com>
Reported-by: syzbot+9c2bdc9d24e4a7abe741@syzkaller.appspotmail.com
Tested-by: Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com>
Cc: Johan Hovold <johan@kernel.org>
Cc: Johan Hovold <johan@kernel.org>
Cc: Luis Chamberlain <mcgrof@kernel.org>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Rudi Heitbaum <rudi@heitbaum.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Vegard Nossum <vegard.nossum@oracle.com>
---
kernel/module/main.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/kernel/module/main.c b/kernel/module/main.c
index d6de66a6a1ac..6100d0373f2f 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -3121,39 +3121,42 @@ static void idempotent_complete(struct idempotent *u, int 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;
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);
mod_stat_add_long(len, &invalid_kread_bytes);
- return len;
+ ret = len;
+ goto out;
}
if (flags & MODULE_INIT_COMPRESSED_FILE) {
int err = module_decompress(&info, buf, len);
vfree(buf); /* compressed data is no longer needed */
if (err) {
mod_stat_inc(&failed_decompress);
mod_stat_add_long(len, &invalid_decompress_bytes);
- return err;
+ ret = err;
+ goto out;
}
} else {
info.hdr = buf;
info.len = len;
}
ret = load_module(&info, uargs, flags);
+out:
idempotent_complete(&idem, ret);
return ret;
}
--
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] module: always complete idempotent loads
2023-07-04 10:08 ` [PATCH] module: always complete idempotent loads Vegard Nossum
@ 2023-07-04 13:37 ` Linus Torvalds
2023-07-04 15:11 ` Vegard Nossum
2023-07-04 18:28 ` Vegard Nossum
0 siblings, 2 replies; 6+ messages in thread
From: Linus Torvalds @ 2023-07-04 13:37 UTC (permalink / raw)
To: Vegard Nossum
Cc: Luis Chamberlain, George Kennedy, linux-kernel, linux-modules,
Harshit Mogalapalli, syzbot+9c2bdc9d24e4a7abe741, Johan Hovold,
Dan Williams, Rudi Heitbaum, David Hildenbrand
[-- Attachment #1: Type: text/plain, Size: 672 bytes --]
On Tue, 4 Jul 2023 at 03:09, Vegard Nossum <vegard.nossum@oracle.com> 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
[-- Attachment #2: patch.diff --]
[-- Type: text/x-patch, Size: 2367 bytes --]
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;
}
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] module: always complete idempotent loads
2023-07-04 13:37 ` Linus Torvalds
@ 2023-07-04 15:11 ` Vegard Nossum
2023-07-04 17:10 ` Linus Torvalds
2023-07-04 18:28 ` Vegard Nossum
1 sibling, 1 reply; 6+ messages in thread
From: Vegard Nossum @ 2023-07-04 15:11 UTC (permalink / raw)
To: Linus Torvalds
Cc: Luis Chamberlain, George Kennedy, linux-kernel, linux-modules,
Harshit Mogalapalli, syzbot+9c2bdc9d24e4a7abe741, Johan Hovold,
Dan Williams, Rudi Heitbaum, David Hildenbrand
On 7/4/23 15:37, Linus Torvalds wrote:
> On Tue, 4 Jul 2023 at 03:09, Vegard Nossum <vegard.nossum@oracle.com> 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?
Looks mostly good. This bit is now included inside the concurrency check:
if (!f || !(f->f_mode & FMODE_READ))
return -EBADF;
Since the cookie is file_inode(f) I think that means that you could have
one caller without FMODE_READ hit this check and it would potentially
return -EBADF even for other callers who did open the file properly.
Maybe just do the f_mode check in finit_module()? Or... new helper,
fdget_mode()??
Apart from this, there is another bit that looks a bit weird:
len = kernel_read_file(f, 0, &buf, INT_MAX, NULL, READING_MODULE);
if (len < 0) {
mod_stat_inc(&failed_kreads);
mod_stat_add_long(len, &invalid_kread_bytes);
I don't think we should be adding error codes to byte counts.
Vegard
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] module: always complete idempotent loads
2023-07-04 15:11 ` Vegard Nossum
@ 2023-07-04 17:10 ` Linus Torvalds
0 siblings, 0 replies; 6+ messages in thread
From: Linus Torvalds @ 2023-07-04 17:10 UTC (permalink / raw)
To: Vegard Nossum
Cc: Luis Chamberlain, George Kennedy, linux-kernel, linux-modules,
Harshit Mogalapalli, syzbot+9c2bdc9d24e4a7abe741, Johan Hovold,
Dan Williams, Rudi Heitbaum, David Hildenbrand
On Tue, 4 Jul 2023 at 08:12, Vegard Nossum <vegard.nossum@oracle.com> wrote:
>
> Maybe just do the f_mode check in finit_module()? Or... new helper,
> fdget_mode()??
I actually wanted to do a fdget_read/write() helper long ago: we
already basically pass in a "this mode is not ok" flag for the
FMODE_PATH case, and it's sad how many extra "do we have
FMODE_READ/WRITE" tests we have when it would actually make tons of
sense to just check it at fdget() time.
But I created the patch, and then decided it was just churn for
something that didn't matter a lot.
The existing "mode" argument to __fget_light() and __fget() would end
up split into "error if these bits are set" (existing) and "error if
these bits are _not_ set" (new) arguments.
It was straightforward, but I looked at the patch, and then looked at
all the existing users that currently check the error separately, and
went "bah", and threw it all away.
Which is not to say it's not the right thing to do. Maybe we should at
some point. But doing it right (ie doing it in that helper before we
even bother with reference counting etc) is just a bit too much work.
The alternative is, of course, to just have a *truly* stupid wrapper
that does the error checking and does the "fdput()" if FMODE_READ
wasn't set. But that's just disgusting.
Anyway, for this module case, I just moved it out to the caller.
> Apart from this, there is another bit that looks a bit weird:
>
> len = kernel_read_file(f, 0, &buf, INT_MAX, NULL, READING_MODULE);
> if (len < 0) {
> mod_stat_inc(&failed_kreads);
> mod_stat_add_long(len, &invalid_kread_bytes);
>
> I don't think we should be adding error codes to byte counts.
Ack. I fixed that at the same time as a "multiple problems with the
error paths".
Linus
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] module: always complete idempotent loads
2023-07-04 13:37 ` Linus Torvalds
2023-07-04 15:11 ` Vegard Nossum
@ 2023-07-04 18:28 ` Vegard Nossum
2023-07-04 18:38 ` Linus Torvalds
1 sibling, 1 reply; 6+ messages in thread
From: Vegard Nossum @ 2023-07-04 18:28 UTC (permalink / raw)
To: Linus Torvalds
Cc: Luis Chamberlain, George Kennedy, linux-kernel, linux-modules,
Harshit Mogalapalli, syzbot+9c2bdc9d24e4a7abe741, Johan Hovold,
Dan Williams, Rudi Heitbaum, David Hildenbrand
On 7/4/23 15:37, Linus Torvalds wrote:
> On Tue, 4 Jul 2023 at 03:09, Vegard Nossum <vegard.nossum@oracle.com> 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?
Harshit tells me there's still a crash... and indeed, with your wrapped
version we call file_inode() on a NULL pointer before checking the
fdget() return value.
It's likely you already changed this with the f_mode changes I commented
on, so maybe it's no longer a problem, though.
Vegard
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] module: always complete idempotent loads
2023-07-04 18:28 ` Vegard Nossum
@ 2023-07-04 18:38 ` Linus Torvalds
0 siblings, 0 replies; 6+ messages in thread
From: Linus Torvalds @ 2023-07-04 18:38 UTC (permalink / raw)
To: Vegard Nossum
Cc: Luis Chamberlain, George Kennedy, linux-kernel, linux-modules,
Harshit Mogalapalli, syzbot+9c2bdc9d24e4a7abe741, Johan Hovold,
Dan Williams, Rudi Heitbaum, David Hildenbrand
On Tue, 4 Jul 2023 at 11:29, Vegard Nossum <vegard.nossum@oracle.com> wrote:
>
> It's likely you already changed this with the f_mode changes I commented
> on, so maybe it's no longer a problem, though.
Yes I did. Let me just push out the stuff I have pending, including
that fixed-up commit.
[ Short time passes ]
Done.
Linus
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2023-07-04 18:38 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <20230704092309.22669-1-vegard.nossum@oracle.com>
2023-07-04 10:08 ` [PATCH] module: always complete idempotent loads Vegard Nossum
2023-07-04 13:37 ` Linus Torvalds
2023-07-04 15:11 ` Vegard Nossum
2023-07-04 17:10 ` Linus Torvalds
2023-07-04 18:28 ` Vegard Nossum
2023-07-04 18:38 ` Linus Torvalds
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®