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

Commit 00d67adb authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "fs/proc: simplify and clarify get_mm_cmdline() function"

parents 3e91991e bcfab573
Loading
Loading
Loading
Loading
+65 −121
Original line number Diff line number Diff line
@@ -209,24 +209,16 @@ static int proc_root_link(struct dentry *dentry, struct path *path)
}

static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf,
			      size_t _count, loff_t *pos)
			      size_t count, loff_t *ppos)
{
	char *page;
	unsigned long count = _count;
	unsigned long arg_start, arg_end, env_start, env_end;
	unsigned long len1, len2, len;
	unsigned long p;
	char c;
	ssize_t rv;
	unsigned long pos, len;
	char *page;

	/* Check if process spawned far enough to have cmdline. */
	if (!mm->env_end)
		return 0;

	page = (char *)__get_free_page(GFP_KERNEL);
	if (!page)
		return -ENOMEM;

	down_read(&mm->mmap_sem);
	arg_start = mm->arg_start;
	arg_end = mm->arg_end;
@@ -234,126 +226,78 @@ static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf,
	env_end = mm->env_end;
	up_read(&mm->mmap_sem);

	BUG_ON(arg_start > arg_end);
	BUG_ON(env_start > env_end);

	len1 = arg_end - arg_start;
	len2 = env_end - env_start;
	if (arg_start >= arg_end)
		return 0;

	/* Empty ARGV. */
	if (len1 == 0) {
		rv = 0;
		goto out_free_page;
	}
	/*
	 * Inherently racy -- command line shares address space
	 * with code and data.
	 * We have traditionally allowed the user to re-write
	 * the argument strings and overflow the end result
	 * into the environment section. But only do that if
	 * the environment area is contiguous to the arguments.
	 */
	rv = access_remote_vm(mm, arg_end - 1, &c, 1, FOLL_ANON);
	if (rv <= 0)
		goto out_free_page;
	if (env_start != arg_end || env_start >= env_end)
		env_start = env_end = arg_end;

	rv = 0;
	/* We're not going to care if "*ppos" has high bits set */
	pos = arg_start + *ppos;

	if (c == '\0') {
		/* Command line (set of strings) occupies whole ARGV. */
		if (len1 <= *pos)
			goto out_free_page;
	/* .. but we do check the result is in the proper range */
	if (pos < arg_start || pos >= env_end)
		return 0;

		p = arg_start + *pos;
		len = len1 - *pos;
		while (count > 0 && len > 0) {
			unsigned int _count;
			int nr_read;
	/* .. and we never go past env_end */
	if (env_end - pos < count)
		count = env_end - pos;

			_count = min3(count, len, PAGE_SIZE);
			nr_read = access_remote_vm(mm, p, page, _count, FOLL_ANON);
			if (nr_read < 0)
				rv = nr_read;
			if (nr_read <= 0)
				goto out_free_page;
	page = (char *)__get_free_page(GFP_KERNEL);
	if (!page)
		return -ENOMEM;

			if (copy_to_user(buf, page, nr_read)) {
				rv = -EFAULT;
				goto out_free_page;
			}
	len = 0;
	while (count) {
		int got;
		size_t size = min_t(size_t, PAGE_SIZE, count);

			p	+= nr_read;
			len	-= nr_read;
			buf	+= nr_read;
			count	-= nr_read;
			rv	+= nr_read;
		}
	} else {
		/*
		 * Command line (1 string) occupies ARGV and
		 * extends into ENVP.
		 */
		struct {
			unsigned long p;
			unsigned long len;
		} cmdline[2] = {
			{ .p = arg_start, .len = len1 },
			{ .p = env_start, .len = len2 },
		};
		loff_t pos1 = *pos;
		unsigned int i;
		got = access_remote_vm(mm, pos, page, size, FOLL_ANON);
		if (got <= 0)
			break;

		i = 0;
		while (i < 2 && pos1 >= cmdline[i].len) {
			pos1 -= cmdline[i].len;
			i++;
		}
		while (i < 2) {
			p = cmdline[i].p + pos1;
			len = cmdline[i].len - pos1;
			while (count > 0 && len > 0) {
				unsigned int _count, l;
				int nr_read;
				bool final;

				_count = min3(count, len, PAGE_SIZE);
				nr_read = access_remote_vm(mm, p, page, _count, FOLL_ANON);
				if (nr_read < 0)
					rv = nr_read;
				if (nr_read <= 0)
					goto out_free_page;
		/* Don't walk past a NUL character once you hit arg_end */
		if (pos + got >= arg_end) {
			int n = 0;

			/*
				 * Command line can be shorter than whole ARGV
				 * even if last "marker" byte says it is not.
			 * If we started before 'arg_end' but ended up
			 * at or after it, we start the NUL character
			 * check at arg_end-1 (where we expect the normal
			 * EOF to be).
			 *
			 * NOTE! This is smaller than 'got', because
			 * pos + got >= arg_end
			 */
				final = false;
				l = strnlen(page, nr_read);
				if (l < nr_read) {
					nr_read = l;
					final = true;
				}
			if (pos < arg_end)
				n = arg_end - pos - 1;

				if (copy_to_user(buf, page, nr_read)) {
					rv = -EFAULT;
					goto out_free_page;
				}

				p	+= nr_read;
				len	-= nr_read;
				buf	+= nr_read;
				count	-= nr_read;
				rv	+= nr_read;

				if (final)
					goto out_free_page;
			/* Cut off at first NUL after 'n' */
			got = n + strnlen(page+n, got-n);
			if (!got)
				break;
		}

			/* Only first chunk can be read partially. */
			pos1 = 0;
			i++;
		got -= copy_to_user(buf, page, got);
		if (unlikely(!got)) {
			if (!len)
				len = -EFAULT;
			break;
		}
		pos += got;
		buf += got;
		len += got;
		count -= got;
	}

out_free_page:
	free_page((unsigned long)page);
	return rv;
	return len;
}

static ssize_t get_task_cmdline(struct task_struct *tsk, char __user *buf,