return result;
  }
  
- static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
-                                    size_t _count, loff_t *pos)
+ static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf,
+                             size_t count, loff_t *ppos)
  {
-       struct task_struct *tsk;
-       struct mm_struct *mm;
-       char *page;
-       unsigned long count = _count;
        unsigned long arg_start, arg_end, env_start, env_end;
-       unsigned long len1, len2;
-       char __user *buf0 = buf;
-       struct {
-               unsigned long p;
-               unsigned long len;
-       } cmdline[2];
-       char c;
-       int rv;
- 
-       BUG_ON(*pos < 0);
+       unsigned long pos, len;
+       char *page;
  
-       tsk = get_proc_task(file_inode(file));
-       if (!tsk)
-               return -ESRCH;
-       mm = get_task_mm(tsk);
-       put_task_struct(tsk);
-       if (!mm)
-               return 0;
        /* Check if process spawned far enough to have cmdline. */
-       if (!mm->env_end) {
-               rv = 0;
-               goto out_mmput;
-       }
- 
-       page = (char *)__get_free_page(GFP_KERNEL);
-       if (!page) {
-               rv = -ENOMEM;
-               goto out_mmput;
-       }
+       if (!mm->env_end)
+               return 0;
  
 -      down_read(&mm->mmap_sem);
 +      spin_lock(&mm->arg_lock);
        arg_start = mm->arg_start;
        arg_end = mm->arg_end;
        env_start = mm->env_start;
        env_end = mm->env_end;
 -      up_read(&mm->mmap_sem);
 +      spin_unlock(&mm->arg_lock);
  
-       BUG_ON(arg_start > arg_end);
-       BUG_ON(env_start > env_end);
- 
-       len1 = arg_end - arg_start;
-       len2 = env_end - env_start;
- 
-       /* Empty ARGV. */
-       if (len1 == 0)
-               goto end;
+       if (arg_start >= arg_end)
+               return 0;
  
        /*
-        * 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.
         */
-       if (access_remote_vm(mm, arg_end - 1, &c, 1, FOLL_ANON) != 1)
-               goto end;
- 
-       cmdline[0].p = arg_start;
-       cmdline[0].len = len1;
-       if (c == '\0') {
-               /* Command line (set of strings) occupies whole ARGV. */
-               cmdline[1].len = 0;
-       } else {
-               /*
-                * Command line (1 string) occupies ARGV and
-                * extends into ENVP.
-                */
-               cmdline[1].p = env_start;
-               cmdline[1].len = len2;
-       }
+       if (env_start != arg_end || env_start >= env_end)
+               env_start = env_end = arg_end;
+ 
+       /* We're not going to care if "*ppos" has high bits set */
+       pos = arg_start + *ppos;
  
-       {
-               loff_t pos1 = *pos;
-               unsigned int i;
+       /* .. but we do check the result is in the proper range */
+       if (pos < arg_start || pos >= env_end)
+               return 0;
  
-               i = 0;
-               while (i < 2 && pos1 >= cmdline[i].len) {
-                       pos1 -= cmdline[i].len;
-                       i++;
+       /* .. and we never go past env_end */
+       if (env_end - pos < count)
+               count = env_end - pos;
+ 
+       page = (char *)__get_free_page(GFP_KERNEL);
+       if (!page)
+               return -ENOMEM;
+ 
+       len = 0;
+       while (count) {
+               int got;
+               size_t size = min_t(size_t, PAGE_SIZE, count);
+ 
+               got = access_remote_vm(mm, pos, page, size, FOLL_ANON);
+               if (got <= 0)
+                       break;
+ 
+               /* Don't walk past a NUL character once you hit arg_end */
+               if (pos + got >= arg_end) {
+                       int n = 0;
+ 
+                       /*
+                        * 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
+                        */
+                       if (pos < arg_end)
+                               n = arg_end - pos - 1;
+ 
+                       /* Cut off at first NUL after 'n' */
+                       got = n + strnlen(page+n, got-n);
+                       if (!got)
+                               break;
                }
-               while (i < 2) {
-                       unsigned long p;
-                       unsigned long len;
- 
-                       p = cmdline[i].p + pos1;
-                       len = cmdline[i].len - pos1;
-                       while (count > 0 && len > 0) {
-                               unsigned int nr_read, nr_write;
- 
-                               nr_read = min3(count, len, PAGE_SIZE);
-                               nr_read = access_remote_vm(mm, p, page, nr_read, FOLL_ANON);
-                               if (nr_read == 0)
-                                       goto end;
- 
-                               /*
-                                * Command line can be shorter than whole ARGV
-                                * even if last "marker" byte says it is not.
-                                */
-                               if (c == '\0')
-                                       nr_write = nr_read;
-                               else
-                                       nr_write = strnlen(page, nr_read);
- 
-                               if (copy_to_user(buf, page, nr_write)) {
-                                       rv = -EFAULT;
-                                       goto out_free_page;
-                               }
- 
-                               p       += nr_write;
-                               len     -= nr_write;
-                               buf     += nr_write;
-                               count   -= nr_write;
- 
-                               if (nr_write < nr_read)
-                                       goto end;
-                       }
- 
-                       /* 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;
        }
  
- end:
-       *pos += buf - buf0;
-       rv = buf - buf0;
- out_free_page:
        free_page((unsigned long)page);
- out_mmput:
+       return len;
+ }
+ 
+ static ssize_t get_task_cmdline(struct task_struct *tsk, char __user *buf,
+                               size_t count, loff_t *pos)
+ {
+       struct mm_struct *mm;
+       ssize_t ret;
+ 
+       mm = get_task_mm(tsk);
+       if (!mm)
+               return 0;
+ 
+       ret = get_mm_cmdline(mm, buf, count, pos);
        mmput(mm);
-       return rv;
+       return ret;
+ }
+ 
+ static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
+                                    size_t count, loff_t *pos)
+ {
+       struct task_struct *tsk;
+       ssize_t ret;
+ 
+       BUG_ON(*pos < 0);
+ 
+       tsk = get_proc_task(file_inode(file));
+       if (!tsk)
+               return -ESRCH;
+       ret = get_task_cmdline(tsk, buf, count, pos);
+       put_task_struct(tsk);
+       if (ret > 0)
+               *pos += ret;
+       return ret;
  }
  
  static const struct file_operations proc_pid_cmdline_ops = {