kexec: avoid compat_alloc_user_space
[linux-2.6-microblaze.git] / kernel / kexec.c
index 9c7aef8..b5e40f0 100644 (file)
 
 #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;
@@ -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. */
@@ -104,7 +85,7 @@ out_free_image:
 }
 
 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;
@@ -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)
@@ -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;
 }
@@ -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);
@@ -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