Re: Hang on large copy_from_user with PREEMPT_NONE

From: Linus Torvalds
Date: Tue Apr 07 2015 - 13:33:57 EST


On Tue, Apr 7, 2015 at 10:00 AM, Sasha Levin <sasha.levin@xxxxxxxxxx> wrote:
>
> It locks up quickly without KASan as well.

I suspect it's some virtualization artifact, where the writes cause
COW faults (or just memory allocations) in the L0 domain.

Whatever. It's probably not worth fighting. Either we just decide that
"copy_from_user()" shouldn't bother to zero huge areas (limit the
zeroing to some arbitrary size), or we just special-case the module
loading.

Something like the attached patch.

Entirely untested, as usual.

Linus
kernel/module.c | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/kernel/module.c b/kernel/module.c
index 99fdf94efce8..ec53f594e9c9 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -2479,6 +2479,23 @@ static int elf_header_check(struct load_info *info)
return 0;
}

+#define COPY_CHUNK_SIZE (16*PAGE_SIZE)
+
+static int copy_chunked_from_user(void *dst, const void __user *usrc, unsigned long len)
+{
+ do {
+ unsigned long n = min(len, COPY_CHUNK_SIZE);
+
+ if (copy_from_user(dst, usrc, n) != 0)
+ return -EFAULT;
+ cond_resched();
+ dst += n;
+ usrc += n;
+ len -= n;
+ } while (len);
+ return 0;
+}
+
/* Sets info->hdr and info->len. */
static int copy_module_from_user(const void __user *umod, unsigned long len,
struct load_info *info)
@@ -2498,7 +2515,7 @@ static int copy_module_from_user(const void __user *umod, unsigned long len,
if (!info->hdr)
return -ENOMEM;

- if (copy_from_user(info->hdr, umod, info->len) != 0) {
+ if (copy_chunked_from_user(info->hdr, umod, info->len) != 0) {
vfree(info->hdr);
return -EFAULT;
}