Loading fs/proc/base.c +65 −121 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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, Loading Loading
fs/proc/base.c +65 −121 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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, Loading