/*
* kexec: kexec_file_load system call
*
* Copyright (C) 2014 Red Hat Inc.
* Authors:
* Vivek Goyal <vgoyal@redhat.com>
*
* This source code is licensed under the GNU General Public License,
* Version 2. See the file COPYING for more details.
*/
#include <linux/capability.h>
#include <linux/mm.h>
#include <linux/file.h>
#include <linux/slab.h>
#include <linux/kexec.h>
#include <linux/mutex.h>
#include <linux/list.h>
#include <crypto/hash.h>
#include <crypto/sha.h>
#include <linux/syscalls.h>
#include <linux/vmalloc.h>
#include "kexec_internal.h"
/*
* Declare these symbols weak so that if architecture provides a purgatory,
* these will be overridden.
*/
char __weak kexec_purgatory[0];
size_t __weak kexec_purgatory_size = 0;
static int kexec_calculate_store_digests(struct kimage *image);
static int copy_file_from_fd(int fd, void **buf, unsigned long *buf_len)
{
struct fd f = fdget(fd);
int ret;
struct kstat stat;
loff_t pos;
ssize_t bytes = 0;
if (!f.file)
return -EBADF;
ret = vfs_getattr(&f.file->f_path, &stat);
if (ret)
goto out;
if (stat.size > INT_MAX) {
ret = -EFBIG;
goto out;
}
/* Don't hand 0 to vmalloc, it whines. */
if (stat.size == 0) {
ret = -EINVAL;
goto out;
}
*buf = vmalloc(stat.size);
if (!*buf) {
ret = -ENOMEM;
goto out;
}
pos = 0;
while (pos < stat.size) {
bytes = kernel_read(f.file, pos, (char *)(*buf) + pos,
stat.size - pos);
if (bytes < 0) {
vfree(*buf);
ret = bytes;
goto out;
}
if (bytes == 0)
break;
pos += bytes;
}
if (pos != stat.size) {
ret = -EBADF;
vfree(*buf);
goto out;
}
*buf_len = pos;
out:
fdput(f);
return ret;
}
/* Architectures can provide this probe function */
int __weak arch_kexec_kernel_image_probe(struct kimage *image, void *buf,
unsigned long buf_len)
{
return -ENOEXEC;
}
void * __weak arch_kexec_kernel_image_load(struct kimage *image)
{
return ERR_PTR(-ENOEXEC);
}
int __weak arch_kimage_file_post_load_cleanup(struct kimage *image)
{
return -EINVAL;
}
int __weak arch_kexec_kernel_verify_sig(struct kimage *image, void *buf,
unsigned long buf_len)
{
return -EKEYREJECTED;
}
/* Apply relocations of type RELA */
int __weak
arch_kexec_apply_relocations_add(const Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
unsigned int relsec)
{
pr_err("RELA relocation unsupported.\n");
return -ENOEXEC;
}
/* Apply relocations of type REL */
int __weak
arch_kexec_apply_relocations(const Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
unsigned int relsec)
{
pr_err("REL relocation unsupported.\n");
return -ENOEXEC;
}
/*
* Free up memory used by kernel, initrd, and command line. This is temporary
* memory allocation which is not needed any more after these buffers have
* been loaded into separate segments and have been copied elsewhere.
*/
void kimage_file_post_load_cleanup(struct kimage *image)
{
struct purgatory_info *pi = &image->purgatory_info;
vfree(image->kernel_buf);
image->kernel_buf = NULL;
vfree(image->initrd_buf);
image->initrd_buf = NULL;
kfree(image->cmdline_buf);
image->cmdline_buf = NULL;
vfree(pi->purgatory_buf);
pi->purgatory_buf = NULL;
vfree(pi->sechdrs);
pi->sechdrs = NULL;
/* See if architecture has anything to cleanup post load */
arch_kimage_file_post_load_cleanup(image);
/*
* Above call should have called into bootloader to free up
* any data stored in kimage->image_loader_data. It should
* be ok now to free it up.
*/
kfree(image->image_loader_data);
image->image_loader_data = NULL;
}
/*
* In file mode list of segments is prepared by kernel. Copy relevant
* data from user space, do error checking, prepare segment list
*/
static int
kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
const char __user *cmdline_ptr,
unsigned long cmdline_len, unsigned flags)
{
int ret = 0;
void *ldata;
ret = copy_file_from_fd(kernel_fd, &image->kernel_buf,
&image->kernel_buf_len);
if (ret)
return ret;
/* Call arch image probe handlers */
ret = arch_kexec_kernel_image_probe(image, image->kernel_buf,
image->kernel_buf_len);
if (ret)
goto out;
#ifdef CONFIG_KEXEC_VERIFY_SIG
ret = arch_kexec_kernel_verify_sig(image, image->kernel_buf,
image->kernel_buf_len);
if (ret) {
pr_debug("kernel signature verification failed.\n");
goto out;
}
pr_debug("kernel signature verification successful.\n");
#endif
/* It is possible that there no initramfs is being loaded */
if (!(flags & KEXEC_FILE_NO_INITRAMFS)) {
ret = copy_file_from_fd(initrd_fd, &image->initrd_buf,
&image-><