mm/kasan/kasan_test_c.c | 4 ++++ mm/maccess.c | 1 - 2 files changed, 4 insertions(+), 1 deletion(-)
`copy_to_kernel_nofault()` is an internal helper which should not be
visible to loadable modules – exporting it would give exploit code a
cheap oracle to probe kernel addresses. Instead, keep the helper
un-exported and compile the kunit case that exercises it only when
`mm/kasan/kasan_test.o` is linked into vmlinux.
Fixes: ca79a00bb9a8 ("kasan: migrate copy_user_test to kunit")
Suggested-by: Christoph Hellwig <hch@infradead.org>
Suggested-by: Marco Elver <elver@google.com>
Signed-off-by: Sabyrzhan Tasbolatov <snovitoll@gmail.com>
---
mm/kasan/kasan_test_c.c | 4 ++++
mm/maccess.c | 1 -
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/mm/kasan/kasan_test_c.c b/mm/kasan/kasan_test_c.c
index 5f922dd38ffa..094ecd27b707 100644
--- a/mm/kasan/kasan_test_c.c
+++ b/mm/kasan/kasan_test_c.c
@@ -1977,6 +1977,7 @@ static void rust_uaf(struct kunit *test)
KUNIT_EXPECT_KASAN_FAIL(test, kasan_test_rust_uaf());
}
+#ifndef MODULE
static void copy_to_kernel_nofault_oob(struct kunit *test)
{
char *ptr;
@@ -2011,6 +2012,7 @@ static void copy_to_kernel_nofault_oob(struct kunit *test)
kfree(ptr);
}
+#endif /* !MODULE */
static void copy_user_test_oob(struct kunit *test)
{
@@ -2131,7 +2133,9 @@ static struct kunit_case kasan_kunit_test_cases[] = {
KUNIT_CASE(match_all_not_assigned),
KUNIT_CASE(match_all_ptr_tag),
KUNIT_CASE(match_all_mem_tag),
+#ifndef MODULE
KUNIT_CASE(copy_to_kernel_nofault_oob),
+#endif
KUNIT_CASE(rust_uaf),
KUNIT_CASE(copy_user_test_oob),
{}
diff --git a/mm/maccess.c b/mm/maccess.c
index 831b4dd7296c..486559d68858 100644
--- a/mm/maccess.c
+++ b/mm/maccess.c
@@ -82,7 +82,6 @@ long copy_to_kernel_nofault(void *dst, const void *src, size_t size)
pagefault_enable();
return -EFAULT;
}
-EXPORT_SYMBOL_GPL(copy_to_kernel_nofault);
long strncpy_from_kernel_nofault(char *dst, const void *unsafe_addr, long count)
{
--
2.34.1
On Sun, Jun 22, 2025 at 7:19 AM Sabyrzhan Tasbolatov <snovitoll@gmail.com> wrote: > > `copy_to_kernel_nofault()` is an internal helper which should not be > visible to loadable modules – exporting it would give exploit code a > cheap oracle to probe kernel addresses. Instead, keep the helper > un-exported and compile the kunit case that exercises it only when > `mm/kasan/kasan_test.o` is linked into vmlinux. > > Fixes: ca79a00bb9a8 ("kasan: migrate copy_user_test to kunit") > Suggested-by: Christoph Hellwig <hch@infradead.org> > Suggested-by: Marco Elver <elver@google.com> > Signed-off-by: Sabyrzhan Tasbolatov <snovitoll@gmail.com> > --- > mm/kasan/kasan_test_c.c | 4 ++++ > mm/maccess.c | 1 - > 2 files changed, 4 insertions(+), 1 deletion(-) > > diff --git a/mm/kasan/kasan_test_c.c b/mm/kasan/kasan_test_c.c > index 5f922dd38ffa..094ecd27b707 100644 > --- a/mm/kasan/kasan_test_c.c > +++ b/mm/kasan/kasan_test_c.c > @@ -1977,6 +1977,7 @@ static void rust_uaf(struct kunit *test) > KUNIT_EXPECT_KASAN_FAIL(test, kasan_test_rust_uaf()); > } > > +#ifndef MODULE Would be great to have a comment here explaining the ifndef. > static void copy_to_kernel_nofault_oob(struct kunit *test) > { > char *ptr; > @@ -2011,6 +2012,7 @@ static void copy_to_kernel_nofault_oob(struct kunit *test) > > kfree(ptr); > } > +#endif /* !MODULE */ > > static void copy_user_test_oob(struct kunit *test) > { > @@ -2131,7 +2133,9 @@ static struct kunit_case kasan_kunit_test_cases[] = { > KUNIT_CASE(match_all_not_assigned), > KUNIT_CASE(match_all_ptr_tag), > KUNIT_CASE(match_all_mem_tag), > +#ifndef MODULE > KUNIT_CASE(copy_to_kernel_nofault_oob), > +#endif > KUNIT_CASE(rust_uaf), > KUNIT_CASE(copy_user_test_oob), > {} > diff --git a/mm/maccess.c b/mm/maccess.c > index 831b4dd7296c..486559d68858 100644 > --- a/mm/maccess.c > +++ b/mm/maccess.c > @@ -82,7 +82,6 @@ long copy_to_kernel_nofault(void *dst, const void *src, size_t size) > pagefault_enable(); > return -EFAULT; > } > -EXPORT_SYMBOL_GPL(copy_to_kernel_nofault); > > long strncpy_from_kernel_nofault(char *dst, const void *unsafe_addr, long count) > { > -- > 2.34.1 > Other than that: Reviewed-by: Andrey Konovalov <andreyknvl@gmail.com> Thank you!
`copy_to_kernel_nofault()` is an internal helper which should not be
visible to loadable modules – exporting it would give exploit code a
cheap oracle to probe kernel addresses. Instead, keep the helper
un-exported and compile the kunit case that exercises it only when
`mm/kasan/kasan_test.o` is linked into vmlinux.
Fixes: ca79a00bb9a8 ("kasan: migrate copy_user_test to kunit")
Suggested-by: Christoph Hellwig <hch@infradead.org>
Suggested-by: Marco Elver <elver@google.com>
Reviewed-by: Andrey Konovalov <andreyknvl@gmail.com>
Signed-off-by: Sabyrzhan Tasbolatov <snovitoll@gmail.com>
---
Changes v2:
- add a brief comment to `#ifndef MODULE`
---
mm/kasan/kasan_test_c.c | 8 ++++++++
mm/maccess.c | 1 -
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/mm/kasan/kasan_test_c.c b/mm/kasan/kasan_test_c.c
index 5f922dd38ffa..2aa12dfa427a 100644
--- a/mm/kasan/kasan_test_c.c
+++ b/mm/kasan/kasan_test_c.c
@@ -1977,6 +1977,11 @@ static void rust_uaf(struct kunit *test)
KUNIT_EXPECT_KASAN_FAIL(test, kasan_test_rust_uaf());
}
+/*
+ * copy_to_kernel_nofault() is an internal helper available when
+ * kasan_test is built-in, so it must not be visible to loadable modules.
+ */
+#ifndef MODULE
static void copy_to_kernel_nofault_oob(struct kunit *test)
{
char *ptr;
@@ -2011,6 +2016,7 @@ static void copy_to_kernel_nofault_oob(struct kunit *test)
kfree(ptr);
}
+#endif /* !MODULE */
static void copy_user_test_oob(struct kunit *test)
{
@@ -2131,7 +2137,9 @@ static struct kunit_case kasan_kunit_test_cases[] = {
KUNIT_CASE(match_all_not_assigned),
KUNIT_CASE(match_all_ptr_tag),
KUNIT_CASE(match_all_mem_tag),
+#ifndef MODULE
KUNIT_CASE(copy_to_kernel_nofault_oob),
+#endif
KUNIT_CASE(rust_uaf),
KUNIT_CASE(copy_user_test_oob),
{}
diff --git a/mm/maccess.c b/mm/maccess.c
index 831b4dd7296c..486559d68858 100644
--- a/mm/maccess.c
+++ b/mm/maccess.c
@@ -82,7 +82,6 @@ long copy_to_kernel_nofault(void *dst, const void *src, size_t size)
pagefault_enable();
return -EFAULT;
}
-EXPORT_SYMBOL_GPL(copy_to_kernel_nofault);
long strncpy_from_kernel_nofault(char *dst, const void *unsafe_addr, long count)
{
--
2.34.1
On 22.06.25 16:11, Sabyrzhan Tasbolatov wrote: > `copy_to_kernel_nofault()` is an internal helper which should not be > visible to loadable modules – exporting it would give exploit code a > cheap oracle to probe kernel addresses. Instead, keep the helper > un-exported and compile the kunit case that exercises it only when > `mm/kasan/kasan_test.o` is linked into vmlinux. > > Fixes: ca79a00bb9a8 ("kasan: migrate copy_user_test to kunit") > Suggested-by: Christoph Hellwig <hch@infradead.org> > Suggested-by: Marco Elver <elver@google.com> > Reviewed-by: Andrey Konovalov <andreyknvl@gmail.com> > Signed-off-by: Sabyrzhan Tasbolatov <snovitoll@gmail.com> > --- Acked-by: David Hildenbrand <david@redhat.com> -- Cheers, David / dhildenb
On Sun, 22 Jun 2025 19:11:42 +0500 Sabyrzhan Tasbolatov <snovitoll@gmail.com> wrote: > `copy_to_kernel_nofault()` is an internal helper which should not be > visible to loadable modules – exporting it would give exploit code a > cheap oracle to probe kernel addresses. Instead, keep the helper > un-exported and compile the kunit case that exercises it only when > `mm/kasan/kasan_test.o` is linked into vmlinux. The recent 707f853d7fa3 ("module: Provide EXPORT_SYMBOL_GPL_FOR_MODULES() helper") quietly added a thing which might be useful here. As far as I understand it, this will permit us to export copy_to_kernel_nofault to kasan_test_c.o and to nothing else. "might". It depends on how "exploit code" might get hold of the symbol. Perhaps you/we can discuss this further. Is the problem that copy_to_kernel_nofault() is non-static? Or it the problem that "exploit code" is itself a kernel module? In other words, a fuller investigation of how this export presently benefits exploiters would help us understand how much EXPORT_SYMBOL_GPL_FOR_MODULES() will improve the situation.
On Sun, Jun 22, 2025 at 11:20 PM Andrew Morton <akpm@linux-foundation.org> wrote: > > On Sun, 22 Jun 2025 19:11:42 +0500 Sabyrzhan Tasbolatov <snovitoll@gmail.com> wrote: > > > `copy_to_kernel_nofault()` is an internal helper which should not be > > visible to loadable modules – exporting it would give exploit code a > > cheap oracle to probe kernel addresses. Instead, keep the helper > > un-exported and compile the kunit case that exercises it only when > > `mm/kasan/kasan_test.o` is linked into vmlinux. > > The recent 707f853d7fa3 ("module: Provide > EXPORT_SYMBOL_GPL_FOR_MODULES() helper") quietly added a thing which > might be useful here. As far as I understand it, this will permit us > to export copy_to_kernel_nofault to kasan_test_c.o and to nothing else. Thanks for letting me know about this new method. I believe, the usage for our case is: EXPORT_SYMBOL_GPL_FOR_MODULES(copy_to_kernel_nofault, "kasan_test"); > > "might". It depends on how "exploit code" might get hold of the > symbol. Perhaps you/we can discuss this further. Is the problem that > copy_to_kernel_nofault() is non-static? Or it the problem that > "exploit code" is itself a kernel module? I haven't verified this, but theoretically, it's a handy “write-anywhere-safely” ROP gadget. Assume the attacker has already gained an arbitrary RW primitive via a UAF/OOB bug. Instead of stitching together prepare_kernel_cred() + commit_creds(), which is a common path of using exported symbols to achieve privilege escalation. This path needs two symbols and register juggling. With exported copy_to_kernel_nofault() they can do this: /* Pseudocode of exploit for a ROP stage running in kernel context */ struct cred *cred = leaked_pointer; rop_call(copy_to_kernel_nofault, &cred->uid, &zero, 4) copy_to_kernel_nofault() disables page-faults around the write, so even if cred corupts a guard-page, the write will not crash. > > In other words, a fuller investigation of how this export presently benefits > exploiters would help us understand how much > EXPORT_SYMBOL_GPL_FOR_MODULES() will improve the situation. > Please let me know if I should send v3 with using EXPORT_SYMBOL_GPL_FOR_MODULES(copy_to_kernel_nofault, "kasan_test");
On Sun, Jun 22, 2025 at 9:09 PM Sabyrzhan Tasbolatov <snovitoll@gmail.com> wrote: > > I haven't verified this, but theoretically, it's a handy > “write-anywhere-safely” ROP gadget. > Assume the attacker has already gained an arbitrary RW primitive > via a UAF/OOB bug. Instead of stitching together > prepare_kernel_cred() + commit_creds(), which is a common path > of using exported symbols to achieve privilege escalation. > This path needs two symbols and register juggling. > With exported copy_to_kernel_nofault() they can do this: > > /* Pseudocode of exploit for a ROP stage running in kernel context */ > struct cred *cred = leaked_pointer; > rop_call(copy_to_kernel_nofault, &cred->uid, &zero, 4) > > copy_to_kernel_nofault() disables page-faults around the write, > so even if cred corupts a guard-page, the write will not crash. Attacker can use copy_to_kernel_nofault without it being exported as well. So I'd say this patch is more of a clean-up of exports.
On Sun, Jun 22, 2025, at 16:11, Sabyrzhan Tasbolatov wrote: > `copy_to_kernel_nofault()` is an internal helper which should not be > visible to loadable modules – exporting it would give exploit code a > cheap oracle to probe kernel addresses. Instead, keep the helper > un-exported and compile the kunit case that exercises it only when > `mm/kasan/kasan_test.o` is linked into vmlinux. > > Fixes: ca79a00bb9a8 ("kasan: migrate copy_user_test to kunit") > Suggested-by: Christoph Hellwig <hch@infradead.org> > Suggested-by: Marco Elver <elver@google.com> > Reviewed-by: Andrey Konovalov <andreyknvl@gmail.com> > Signed-off-by: Sabyrzhan Tasbolatov <snovitoll@gmail.com> Acked-by: Arnd Bergmann <arnd@arndb.de>
© 2016 - 2025 Red Hat, Inc.