Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a1e7e2d4 authored by Oleg Drokin's avatar Oleg Drokin Committed by Greg Kroah-Hartman
Browse files

staging/lustre: Fix unsafe userspace access in many proc files



Apparently we are pretty bad about verifying our buffers passed
from userspace.

Signed-off-by: default avatarOleg Drokin <oleg.drokin@intel.com>
Reviewed-on: http://review.whamcloud.com/9059
Intel-bug-id: https://jira.hpdd.intel.com/browse/LU-4563


Reviewed-by: default avatarDmitry Eremin <dmitry.eremin@intel.com>
Reviewed-by: default avatarJames Simmons <uja.ornl@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 05289927
Loading
Loading
Loading
Loading
+29 −12
Original line number Diff line number Diff line
@@ -54,31 +54,48 @@
#include <lustre_fid.h>
#include "fid_internal.h"

/* Format: [0x64BIT_INT - 0x64BIT_INT] + 32 bytes just in case */
#define MAX_FID_RANGE_STRLEN (32 + 2 * 2 * sizeof(__u64))
/*
 * Note: this function is only used for testing, it is no safe for production
 * use.
 */
static int
lprocfs_fid_write_common(const char *buffer, unsigned long count,
static int lprocfs_fid_write_common(const char __user *buffer, size_t count,
				    struct lu_seq_range *range)
{
	struct lu_seq_range tmp;
	int rc;
	char kernbuf[MAX_FID_RANGE_STRLEN];

	LASSERT(range != NULL);

	rc = sscanf(buffer, "[%llx - %llx]\n",
	if (count >= sizeof(kernbuf))
		return -EINVAL;

	if (copy_from_user(kernbuf, buffer, count))
		return -EFAULT;

	kernbuf[count] = 0;

	if (count == 5 && strcmp(kernbuf, "clear") == 0) {
		memset(range, 0, sizeof(*range));
		return count;
	}

	/* of the form "[0x0000000240000400 - 0x000000028000400]" */
	rc = sscanf(kernbuf, "[%llx - %llx]\n",
		    (long long unsigned *)&tmp.lsr_start,
		    (long long unsigned *)&tmp.lsr_end);
	if (rc != 2 || !range_is_sane(&tmp) || range_is_zero(&tmp))
	if (!range_is_sane(&tmp) || range_is_zero(&tmp) ||
	    tmp.lsr_start < range->lsr_start || tmp.lsr_end > range->lsr_end)
		return -EINVAL;
	*range = tmp;
	return 0;
	return count;
}

/* Client side procfs stuff */
static ssize_t
lprocfs_fid_space_seq_write(struct file *file, const char *buffer,
static ssize_t lprocfs_fid_space_seq_write(struct file *file,
					   const char __user *buffer,
					   size_t count, loff_t *off)
{
	struct lu_client_seq *seq = ((struct seq_file *)file->private_data)->private;
@@ -114,8 +131,8 @@ lprocfs_fid_space_seq_show(struct seq_file *m, void *unused)
	return rc;
}

static ssize_t
lprocfs_fid_width_seq_write(struct file *file, const char *buffer,
static ssize_t lprocfs_fid_width_seq_write(struct file *file,
					   const char __user *buffer,
					   size_t count, loff_t *off)
{
	struct lu_client_seq *seq = ((struct seq_file *)file->private_data)->private;
+1 −1
Original line number Diff line number Diff line
@@ -265,7 +265,7 @@ static inline __u64 range_space(const struct lu_seq_range *range)

static inline void range_init(struct lu_seq_range *range)
{
	range->lsr_start = range->lsr_end = range->lsr_index = 0;
	memset(range, 0, sizeof(*range));
}

/**
+73 −18
Original line number Diff line number Diff line
@@ -367,7 +367,8 @@ static int ll_max_cached_mb_seq_show(struct seq_file *m, void *v)
			cache->ccc_lru_shrinkers);
}

static ssize_t ll_max_cached_mb_seq_write(struct file *file, const char *buffer,
static ssize_t ll_max_cached_mb_seq_write(struct file *file,
					  const char __user *buffer,
					  size_t count, loff_t *off)
{
	struct super_block *sb = ((struct seq_file *)file->private_data)->private;
@@ -376,9 +377,18 @@ static ssize_t ll_max_cached_mb_seq_write(struct file *file, const char *buffer,
	int mult, rc, pages_number;
	int diff = 0;
	int nrpages = 0;
	char kernbuf[128];

	if (count >= sizeof(kernbuf))
		return -EINVAL;

	if (copy_from_user(kernbuf, buffer, count))
		return -EFAULT;
	kernbuf[count] = 0;

	mult = 1 << (20 - PAGE_CACHE_SHIFT);
	buffer = lprocfs_find_named_value(buffer, "max_cached_mb:", &count);
	buffer += lprocfs_find_named_value(kernbuf, "max_cached_mb:", &count) -
		  kernbuf;
	rc = lprocfs_write_frac_helper(buffer, count, &pages_number, mult);
	if (rc)
		return rc;
@@ -1163,7 +1173,8 @@ static int ll_rw_extents_stats_pp_seq_show(struct seq_file *seq, void *v)
}

static ssize_t ll_rw_extents_stats_pp_seq_write(struct file *file,
						const char *buf, size_t len,
						const char __user *buf,
						size_t len,
						loff_t *off)
{
	struct seq_file *seq = file->private_data;
@@ -1172,10 +1183,24 @@ static ssize_t ll_rw_extents_stats_pp_seq_write(struct file *file,
	int i;
	int value = 1, rc = 0;

	if (len == 0)
		return -EINVAL;

	rc = lprocfs_write_helper(buf, len, &value);
	if (rc < 0 && (strcmp(buf, "disabled") == 0 ||
		       strcmp(buf, "Disabled") == 0))
	if (rc < 0 && len < 16) {
		char kernbuf[16];

		if (copy_from_user(kernbuf, buf, len))
			return -EFAULT;
		kernbuf[len] = 0;

		if (kernbuf[len - 1] == '\n')
			kernbuf[len - 1] = 0;

		if (strcmp(kernbuf, "disabled") == 0 ||
		    strcmp(kernbuf, "Disabled") == 0)
			value = 0;
	}

	if (value == 0)
		sbi->ll_rw_stats_on = 0;
@@ -1222,7 +1247,8 @@ static int ll_rw_extents_stats_seq_show(struct seq_file *seq, void *v)
	return 0;
}

static ssize_t ll_rw_extents_stats_seq_write(struct file *file, const char *buf,
static ssize_t ll_rw_extents_stats_seq_write(struct file *file,
					     const char __user *buf,
					     size_t len, loff_t *off)
{
	struct seq_file *seq = file->private_data;
@@ -1231,15 +1257,30 @@ static ssize_t ll_rw_extents_stats_seq_write(struct file *file, const char *buf,
	int i;
	int value = 1, rc = 0;

	if (len == 0)
		return -EINVAL;

	rc = lprocfs_write_helper(buf, len, &value);
	if (rc < 0 && (strcmp(buf, "disabled") == 0 ||
		       strcmp(buf, "Disabled") == 0))
	if (rc < 0 && len < 16) {
		char kernbuf[16];

		if (copy_from_user(kernbuf, buf, len))
			return -EFAULT;
		kernbuf[len] = 0;

		if (kernbuf[len - 1] == '\n')
			kernbuf[len - 1] = 0;

		if (strcmp(kernbuf, "disabled") == 0 ||
		    strcmp(kernbuf, "Disabled") == 0)
			value = 0;
	}

	if (value == 0)
		sbi->ll_rw_stats_on = 0;
	else
		sbi->ll_rw_stats_on = 1;

	spin_lock(&sbi->ll_pp_extent_lock);
	for (i = 0; i <= LL_PROCESS_HIST_MAX; i++) {
		io_extents->pp_extents[i].pid = 0;
@@ -1250,7 +1291,6 @@ static ssize_t ll_rw_extents_stats_seq_write(struct file *file, const char *buf,

	return len;
}

LPROC_SEQ_FOPS(ll_rw_extents_stats);

void ll_rw_stats_tally(struct ll_sb_info *sbi, pid_t pid,
@@ -1410,7 +1450,8 @@ static int ll_rw_offset_stats_seq_show(struct seq_file *seq, void *v)
	return 0;
}

static ssize_t ll_rw_offset_stats_seq_write(struct file *file, const char *buf,
static ssize_t ll_rw_offset_stats_seq_write(struct file *file,
					    const char __user *buf,
					    size_t len, loff_t *off)
{
	struct seq_file *seq = file->private_data;
@@ -1419,11 +1460,25 @@ static ssize_t ll_rw_offset_stats_seq_write(struct file *file, const char *buf,
	struct ll_rw_process_info *offset_info = sbi->ll_rw_offset_info;
	int value = 1, rc = 0;

	if (len == 0)
		return -EINVAL;

	rc = lprocfs_write_helper(buf, len, &value);

	if (rc < 0 && (strcmp(buf, "disabled") == 0 ||
			   strcmp(buf, "Disabled") == 0))
	if (rc < 0 && len < 16) {
		char kernbuf[16];

		if (copy_from_user(kernbuf, buf, len))
			return -EFAULT;
		kernbuf[len] = 0;

		if (kernbuf[len - 1] == '\n')
			kernbuf[len - 1] = 0;

		if (strcmp(kernbuf, "disabled") == 0 ||
		    strcmp(kernbuf, "Disabled") == 0)
			value = 0;
	}

	if (value == 0)
		sbi->ll_rw_stats_on = 0;
+8 −1
Original line number Diff line number Diff line
@@ -279,8 +279,15 @@ static ssize_t obd_proc_jobid_var_seq_write(struct file *file, const char *buffe
		return -EINVAL;

	memset(obd_jobid_var, 0, JOBSTATS_JOBID_VAR_MAX_LEN + 1);

	/* This might leave the var invalid on error, which is probably fine.*/
	if (copy_from_user(obd_jobid_var, buffer, count))
		return -EFAULT;

	/* Trim the trailing '\n' if any */
	memcpy(obd_jobid_var, buffer, count - (buffer[count - 1] == '\n'));
	if (obd_jobid_var[count - 1] == '\n')
		obd_jobid_var[count - 1] = 0;

	return count;
}
LPROC_SEQ_FOPS(obd_proc_jobid_var);
+13 −3
Original line number Diff line number Diff line
@@ -174,15 +174,25 @@ static int osc_cached_mb_seq_show(struct seq_file *m, void *v)
}

/* shrink the number of caching pages to a specific number */
static ssize_t osc_cached_mb_seq_write(struct file *file, const char *buffer,
static ssize_t osc_cached_mb_seq_write(struct file *file,
				       const char __user *buffer,
				       size_t count, loff_t *off)
{
	struct obd_device *dev = ((struct seq_file *)file->private_data)->private;
	struct client_obd *cli = &dev->u.cli;
	int pages_number, mult, rc;
	char kernbuf[128];

	if (count >= sizeof(kernbuf))
		return -EINVAL;

	if (copy_from_user(kernbuf, buffer, count))
		return -EFAULT;
	kernbuf[count] = 0;

	mult = 1 << (20 - PAGE_CACHE_SHIFT);
	buffer = lprocfs_find_named_value(buffer, "used_mb:", &count);
	buffer += lprocfs_find_named_value(kernbuf, "used_mb:", &count) -
		  kernbuf;
	rc = lprocfs_write_frac_helper(buffer, count, &pages_number, mult);
	if (rc)
		return rc;