From 63dd14999c6b210fbe33f780fec53faefa867d95 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 13 Jul 2019 14:27:14 -0700 Subject: [PATCH 2/2] /proc//cmdline: add back the setproctitle() special case This makes the setproctitle() special case very explicit indeed, and handles it with a separate helper function entirely. This makes the logic about when we use the string lengths etc much more obvious, and makes it easy to see what we do. [ Fixed for missing 'count' check noted by Alexey Izbyshev ] Signed-off-by: Linus Torvalds --- fs/proc/base.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index 8040f9d1cf07..3ad3ff4cc12c 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -209,12 +209,54 @@ static int proc_root_link(struct dentry *dentry, struct path *path) return result; } +/* + * If the user used setproctitle(), we just get the string from + * user space at arg_start, and limit it to a maximum of one page. + */ +static ssize_t get_mm_proctitle(struct mm_struct *mm, char __user *buf, + size_t count, loff_t *ppos, + unsigned long arg_start) +{ + unsigned long pos = *ppos; + char *page; + int ret, got; + + if (pos >= PAGE_SIZE) + return 0; + + page = (char *)__get_free_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + ret = 0; + got = access_remote_vm(mm, arg_start, page, PAGE_SIZE, FOLL_ANON); + if (got > 0) { + int len = strnlen(page, got); + + /* Include the NUL character if it was found */ + if (len < got) + len++; + + if (len > pos) { + len -= pos; + if (len > count) + len = count; + len -= copy_to_user(buf, page+pos, len); + if (!len) + len = -EFAULT; + ret = len; + } + } + free_page((unsigned long)page); + return ret; +} + static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf, size_t count, loff_t *ppos) { unsigned long arg_start, arg_end; unsigned long pos, len; - char *page; + char *page, c; /* Check if process spawned far enough to have cmdline. */ if (!mm->env_end) @@ -228,6 +270,16 @@ static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf, if (arg_start >= arg_end) return 0; + /* + * Magical special case: if the argv[] end byte is not + * zero, the user has overwritten it with setproctitle(3). + * + * Possible future enhancement: do this only once when + * pos is 0, and set a flag in the 'struct file'. + */ + if (access_remote_vm(mm, arg_end-1, &c, 1, FOLL_ANON) == 1 && c) + return get_mm_proctitle(mm, buf, count, ppos, arg_start); + /* We're not going to care if "*ppos" has high bits set */ /* .. but we do check the result is in the proper range */ pos = arg_start + *ppos; -- 2.22.0.193.g083935f9a2