cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

kernel_read_file.c (4486B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2#include <linux/fs.h>
      3#include <linux/fs_struct.h>
      4#include <linux/kernel_read_file.h>
      5#include <linux/security.h>
      6#include <linux/vmalloc.h>
      7
      8/**
      9 * kernel_read_file() - read file contents into a kernel buffer
     10 *
     11 * @file	file to read from
     12 * @offset	where to start reading from (see below).
     13 * @buf		pointer to a "void *" buffer for reading into (if
     14 *		*@buf is NULL, a buffer will be allocated, and
     15 *		@buf_size will be ignored)
     16 * @buf_size	size of buf, if already allocated. If @buf not
     17 *		allocated, this is the largest size to allocate.
     18 * @file_size	if non-NULL, the full size of @file will be
     19 *		written here.
     20 * @id		the kernel_read_file_id identifying the type of
     21 *		file contents being read (for LSMs to examine)
     22 *
     23 * @offset must be 0 unless both @buf and @file_size are non-NULL
     24 * (i.e. the caller must be expecting to read partial file contents
     25 * via an already-allocated @buf, in at most @buf_size chunks, and
     26 * will be able to determine when the entire file was read by
     27 * checking @file_size). This isn't a recommended way to read a
     28 * file, though, since it is possible that the contents might
     29 * change between calls to kernel_read_file().
     30 *
     31 * Returns number of bytes read (no single read will be bigger
     32 * than INT_MAX), or negative on error.
     33 *
     34 */
     35int kernel_read_file(struct file *file, loff_t offset, void **buf,
     36		     size_t buf_size, size_t *file_size,
     37		     enum kernel_read_file_id id)
     38{
     39	loff_t i_size, pos;
     40	size_t copied;
     41	void *allocated = NULL;
     42	bool whole_file;
     43	int ret;
     44
     45	if (offset != 0 && (!*buf || !file_size))
     46		return -EINVAL;
     47
     48	if (!S_ISREG(file_inode(file)->i_mode))
     49		return -EINVAL;
     50
     51	ret = deny_write_access(file);
     52	if (ret)
     53		return ret;
     54
     55	i_size = i_size_read(file_inode(file));
     56	if (i_size <= 0) {
     57		ret = -EINVAL;
     58		goto out;
     59	}
     60	/* The file is too big for sane activities. */
     61	if (i_size > INT_MAX) {
     62		ret = -EFBIG;
     63		goto out;
     64	}
     65	/* The entire file cannot be read in one buffer. */
     66	if (!file_size && offset == 0 && i_size > buf_size) {
     67		ret = -EFBIG;
     68		goto out;
     69	}
     70
     71	whole_file = (offset == 0 && i_size <= buf_size);
     72	ret = security_kernel_read_file(file, id, whole_file);
     73	if (ret)
     74		goto out;
     75
     76	if (file_size)
     77		*file_size = i_size;
     78
     79	if (!*buf)
     80		*buf = allocated = vmalloc(i_size);
     81	if (!*buf) {
     82		ret = -ENOMEM;
     83		goto out;
     84	}
     85
     86	pos = offset;
     87	copied = 0;
     88	while (copied < buf_size) {
     89		ssize_t bytes;
     90		size_t wanted = min_t(size_t, buf_size - copied,
     91					      i_size - pos);
     92
     93		bytes = kernel_read(file, *buf + copied, wanted, &pos);
     94		if (bytes < 0) {
     95			ret = bytes;
     96			goto out_free;
     97		}
     98
     99		if (bytes == 0)
    100			break;
    101		copied += bytes;
    102	}
    103
    104	if (whole_file) {
    105		if (pos != i_size) {
    106			ret = -EIO;
    107			goto out_free;
    108		}
    109
    110		ret = security_kernel_post_read_file(file, *buf, i_size, id);
    111	}
    112
    113out_free:
    114	if (ret < 0) {
    115		if (allocated) {
    116			vfree(*buf);
    117			*buf = NULL;
    118		}
    119	}
    120
    121out:
    122	allow_write_access(file);
    123	return ret == 0 ? copied : ret;
    124}
    125EXPORT_SYMBOL_GPL(kernel_read_file);
    126
    127int kernel_read_file_from_path(const char *path, loff_t offset, void **buf,
    128			       size_t buf_size, size_t *file_size,
    129			       enum kernel_read_file_id id)
    130{
    131	struct file *file;
    132	int ret;
    133
    134	if (!path || !*path)
    135		return -EINVAL;
    136
    137	file = filp_open(path, O_RDONLY, 0);
    138	if (IS_ERR(file))
    139		return PTR_ERR(file);
    140
    141	ret = kernel_read_file(file, offset, buf, buf_size, file_size, id);
    142	fput(file);
    143	return ret;
    144}
    145EXPORT_SYMBOL_GPL(kernel_read_file_from_path);
    146
    147int kernel_read_file_from_path_initns(const char *path, loff_t offset,
    148				      void **buf, size_t buf_size,
    149				      size_t *file_size,
    150				      enum kernel_read_file_id id)
    151{
    152	struct file *file;
    153	struct path root;
    154	int ret;
    155
    156	if (!path || !*path)
    157		return -EINVAL;
    158
    159	task_lock(&init_task);
    160	get_fs_root(init_task.fs, &root);
    161	task_unlock(&init_task);
    162
    163	file = file_open_root(&root, path, O_RDONLY, 0);
    164	path_put(&root);
    165	if (IS_ERR(file))
    166		return PTR_ERR(file);
    167
    168	ret = kernel_read_file(file, offset, buf, buf_size, file_size, id);
    169	fput(file);
    170	return ret;
    171}
    172EXPORT_SYMBOL_GPL(kernel_read_file_from_path_initns);
    173
    174int kernel_read_file_from_fd(int fd, loff_t offset, void **buf,
    175			     size_t buf_size, size_t *file_size,
    176			     enum kernel_read_file_id id)
    177{
    178	struct fd f = fdget(fd);
    179	int ret = -EBADF;
    180
    181	if (!f.file || !(f.file->f_mode & FMODE_READ))
    182		goto out;
    183
    184	ret = kernel_read_file(f.file, offset, buf, buf_size, file_size, id);
    185out:
    186	fdput(f);
    187	return ret;
    188}
    189EXPORT_SYMBOL_GPL(kernel_read_file_from_fd);