Skip to content

Commit

Permalink
kexec: avoid compat_alloc_user_space
Browse files Browse the repository at this point in the history
kimage_alloc_init() expects a __user pointer, so compat_sys_kexec_load()
uses compat_alloc_user_space() to convert the layout and put it back onto
the user space caller stack.

Moving the user space access into the syscall handler directly actually
makes the code simpler, as the conversion for compat mode can now be done
on kernel memory.

Link: https://lkml.kernel.org/r/[email protected]
Link: https://lore.kernel.org/lkml/YPbtsU4GX6PL7%[email protected]/
Link: https://lore.kernel.org/lkml/[email protected]/
Signed-off-by: Arnd Bergmann <[email protected]>
Co-developed-by: Eric Biederman <[email protected]>
Co-developed-by: Christoph Hellwig <[email protected]>
Acked-by: "Eric W. Biederman" <[email protected]>
Cc: Al Viro <[email protected]>
Cc: Benjamin Herrenschmidt <[email protected]>
Cc: Borislav Petkov <[email protected]>
Cc: Catalin Marinas <[email protected]>
Cc: Christian Borntraeger <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: "David S. Miller" <[email protected]>
Cc: Feng Tang <[email protected]>
Cc: Heiko Carstens <[email protected]>
Cc: Helge Deller <[email protected]>
Cc: "H. Peter Anvin" <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: "James E.J. Bottomley" <[email protected]>
Cc: Michael Ellerman <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Thomas Bogendoerfer <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Vasily Gorbik <[email protected]>
Cc: Will Deacon <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
arndb authored and torvalds committed Sep 8, 2021
1 parent 4b692e8 commit 5d700a0
Showing 1 changed file with 25 additions and 36 deletions.
61 changes: 25 additions & 36 deletions kernel/kexec.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,9 @@

#include "kexec_internal.h"

static int copy_user_segment_list(struct kimage *image,
unsigned long nr_segments,
struct kexec_segment __user *segments)
{
int ret;
size_t segment_bytes;

/* Read in the segments */
image->nr_segments = nr_segments;
segment_bytes = nr_segments * sizeof(*segments);
ret = copy_from_user(image->segment, segments, segment_bytes);
if (ret)
ret = -EFAULT;

return ret;
}

static int kimage_alloc_init(struct kimage **rimage, unsigned long entry,
unsigned long nr_segments,
struct kexec_segment __user *segments,
struct kexec_segment *segments,
unsigned long flags)
{
int ret;
Expand All @@ -58,10 +41,8 @@ static int kimage_alloc_init(struct kimage **rimage, unsigned long entry,
return -ENOMEM;

image->start = entry;

ret = copy_user_segment_list(image, nr_segments, segments);
if (ret)
goto out_free_image;
image->nr_segments = nr_segments;
memcpy(image->segment, segments, nr_segments * sizeof(*segments));

if (kexec_on_panic) {
/* Enable special crash kernel control page alloc policy. */
Expand Down Expand Up @@ -104,7 +85,7 @@ static int kimage_alloc_init(struct kimage **rimage, unsigned long entry,
}

static int do_kexec_load(unsigned long entry, unsigned long nr_segments,
struct kexec_segment __user *segments, unsigned long flags)
struct kexec_segment *segments, unsigned long flags)
{
struct kimage **dest_image, *image;
unsigned long i;
Expand Down Expand Up @@ -250,7 +231,8 @@ static inline int kexec_load_check(unsigned long nr_segments,
SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments,
struct kexec_segment __user *, segments, unsigned long, flags)
{
int result;
struct kexec_segment *ksegments;
unsigned long result;

result = kexec_load_check(nr_segments, flags);
if (result)
Expand All @@ -261,7 +243,12 @@ SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments,
((flags & KEXEC_ARCH_MASK) != KEXEC_ARCH_DEFAULT))
return -EINVAL;

result = do_kexec_load(entry, nr_segments, segments, flags);
ksegments = memdup_user(segments, nr_segments * sizeof(ksegments[0]));
if (IS_ERR(ksegments))
return PTR_ERR(ksegments);

result = do_kexec_load(entry, nr_segments, ksegments, flags);
kfree(ksegments);

return result;
}
Expand All @@ -273,7 +260,7 @@ COMPAT_SYSCALL_DEFINE4(kexec_load, compat_ulong_t, entry,
compat_ulong_t, flags)
{
struct compat_kexec_segment in;
struct kexec_segment out, __user *ksegments;
struct kexec_segment *ksegments;
unsigned long i, result;

result = kexec_load_check(nr_segments, flags);
Expand All @@ -286,24 +273,26 @@ COMPAT_SYSCALL_DEFINE4(kexec_load, compat_ulong_t, entry,
if ((flags & KEXEC_ARCH_MASK) == KEXEC_ARCH_DEFAULT)
return -EINVAL;

ksegments = compat_alloc_user_space(nr_segments * sizeof(out));
ksegments = kmalloc_array(nr_segments, sizeof(ksegments[0]),
GFP_KERNEL);
if (!ksegments)
return -ENOMEM;

for (i = 0; i < nr_segments; i++) {
result = copy_from_user(&in, &segments[i], sizeof(in));
if (result)
return -EFAULT;
goto fail;

out.buf = compat_ptr(in.buf);
out.bufsz = in.bufsz;
out.mem = in.mem;
out.memsz = in.memsz;

result = copy_to_user(&ksegments[i], &out, sizeof(out));
if (result)
return -EFAULT;
ksegments[i].buf = compat_ptr(in.buf);
ksegments[i].bufsz = in.bufsz;
ksegments[i].mem = in.mem;
ksegments[i].memsz = in.memsz;
}

result = do_kexec_load(entry, nr_segments, ksegments, flags);

fail:
kfree(ksegments);
return result;
}
#endif

0 comments on commit 5d700a0

Please sign in to comment.