[PATCH v2] mm: unexport globally copy_to_kernel_nofault

Sabyrzhan Tasbolatov posted 1 patch 3 months, 2 weeks ago
mm/kasan/kasan_test_c.c | 8 ++++++++
mm/maccess.c            | 1 -
2 files changed, 8 insertions(+), 1 deletion(-)
[PATCH v2] mm: unexport globally copy_to_kernel_nofault
Posted by Sabyrzhan Tasbolatov 3 months, 2 weeks ago
`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

Re: [PATCH v2] mm: unexport globally copy_to_kernel_nofault
Posted by David Hildenbrand 3 months, 2 weeks ago
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

Re: [PATCH v2] mm: unexport globally copy_to_kernel_nofault
Posted by Andrew Morton 3 months, 2 weeks ago
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.

Re: [PATCH v2] mm: unexport globally copy_to_kernel_nofault
Posted by Sabyrzhan Tasbolatov 3 months, 2 weeks ago
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");
Re: [PATCH v2] mm: unexport globally copy_to_kernel_nofault
Posted by Andrey Konovalov 3 months, 2 weeks ago
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.
Re: [PATCH v2] mm: unexport globally copy_to_kernel_nofault
Posted by Arnd Bergmann 3 months, 2 weeks ago
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>