exec: Force single empty string when argv is empty
[sfrench/cifs-2.6.git] / fs / exec.c
index 79f2c9483302df9bbf0f37f507022c28ae38c177..40b1008fb0f7950985c02d7be25ff48877cf4a29 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -495,8 +495,14 @@ static int bprm_stack_limits(struct linux_binprm *bprm)
         * the stack. They aren't stored until much later when we can't
         * signal to the parent that the child has run out of stack space.
         * Instead, calculate it here so it's possible to fail gracefully.
+        *
+        * In the case of argc = 0, make sure there is space for adding a
+        * empty string (which will bump argc to 1), to ensure confused
+        * userspace programs don't start processing from argv[1], thinking
+        * argc can never be 0, to keep them from walking envp by accident.
+        * See do_execveat_common().
         */
-       ptr_size = (bprm->argc + bprm->envc) * sizeof(void *);
+       ptr_size = (max(bprm->argc, 1) + bprm->envc) * sizeof(void *);
        if (limit <= ptr_size)
                return -E2BIG;
        limit -= ptr_size;
@@ -1897,6 +1903,9 @@ static int do_execveat_common(int fd, struct filename *filename,
        }
 
        retval = count(argv, MAX_ARG_STRINGS);
+       if (retval == 0)
+               pr_warn_once("process '%s' launched '%s' with NULL argv: empty string added\n",
+                            current->comm, bprm->filename);
        if (retval < 0)
                goto out_free;
        bprm->argc = retval;
@@ -1923,6 +1932,19 @@ static int do_execveat_common(int fd, struct filename *filename,
        if (retval < 0)
                goto out_free;
 
+       /*
+        * When argv is empty, add an empty string ("") as argv[0] to
+        * ensure confused userspace programs that start processing
+        * from argv[1] won't end up walking envp. See also
+        * bprm_stack_limits().
+        */
+       if (bprm->argc == 0) {
+               retval = copy_string_kernel("", bprm);
+               if (retval < 0)
+                       goto out_free;
+               bprm->argc = 1;
+       }
+
        retval = bprm_execve(bprm, fd, filename, flags);
 out_free:
        free_bprm(bprm);
@@ -1951,6 +1973,8 @@ int kernel_execve(const char *kernel_filename,
        }
 
        retval = count_strings_kernel(argv);
+       if (WARN_ON_ONCE(retval == 0))
+               retval = -EINVAL;
        if (retval < 0)
                goto out_free;
        bprm->argc = retval;