From patchwork Mon Jan 25 14:24:04 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adhemerval Zanella X-Patchwork-Id: 60368 Delivered-To: patch@linaro.org Received: by 10.112.130.2 with SMTP id oa2csp1387607lbb; Mon, 25 Jan 2016 06:24:30 -0800 (PST) X-Received: by 10.67.7.200 with SMTP id de8mr26297392pad.28.1453731870786; Mon, 25 Jan 2016 06:24:30 -0800 (PST) Return-Path: Received: from sourceware.org (server1.sourceware.org. [209.132.180.131]) by mx.google.com with ESMTPS id tj1si5165407pab.206.2016.01.25.06.24.30 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 25 Jan 2016 06:24:30 -0800 (PST) Received-SPF: pass (google.com: domain of libc-alpha-return-66559-patch=linaro.org@sourceware.org designates 209.132.180.131 as permitted sender) client-ip=209.132.180.131; Authentication-Results: mx.google.com; spf=pass (google.com: domain of libc-alpha-return-66559-patch=linaro.org@sourceware.org designates 209.132.180.131 as permitted sender) smtp.mailfrom=libc-alpha-return-66559-patch=linaro.org@sourceware.org; dkim=pass header.i=@sourceware.org DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:subject:date:message-id; q=dns; s= default; b=R4vb6eNkLVIDYz/FknGFKylV7q/d+ASGd5DufjRPlwrvGyuV9Edes xZsI/1J9uQOBL04ve8WPkFqOz/RNhvrNCfrtxl0Ei8cOcrBQxeGcCGQtS8s2IKsX WVXiSiJ+qMRgRGnNvQIAP/9GDiTfPaYHU20UVAJZ+iGiWOmdstHm90= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:subject:date:message-id; s=default; bh=d904JuyhR0EdWG578BH5v4Y6Bv4=; b=ilHmkcFKo/VHoeR1GmlpHcqVVVA7 HFLQacPpa3Qo6r82ZbRkDjXOC5avd340NFtax3h9dydMUjO5Ub6yMQSArpNM3vki HOCEdxxQsMYo1nWs2HRGvcJYmW4343okjrKNLGoJSiLw8Cvvq8EoV9GxMDaDnJci UFWnkHxY+eMwje0= Received: (qmail 76899 invoked by alias); 25 Jan 2016 14:24:20 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 76868 invoked by uid 89); 25 Jan 2016 14:24:18 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_LOW, SPF_PASS autolearn=ham version=3.3.2 spammy=shebang, filesystems, *const, stranger X-HELO: mail-yk0-f179.google.com X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:subject:date:message-id; bh=nKXihA7ixK8M3kWFZXGVqeTqjomvRyKLucBU2OzOMbk=; b=hsB9UfPlFjiE/02v5TDej2P8oHLnYikMA6EQuYn5BU+xoB3P2oQ5YN8FLsRA2bXr6t 1DcNe+TuOvkbBadIdWzr82YPFrCamoa48wLzC7wb3pCaDQLz/jHhWkmJy6KEkfYHWQCU ZaS5iTs2+GkXdMa9Wr70fmePNaCDfAgHzZOR3tFnoLi8ClAYGQ32oQnhjrkOJPBR36WD YXkHJbQqM5JvPMupONDdN1bKEGdM5fB39XzMUpDOgYaTIQiqkwvW5L53QDwz9wVW9wA+ Gok062YOFyjqbIiQlwiu/BhPUdIW4LFljEU3T5oN9ZmWUbONx6ieF3sYJEqW1vjGi7bH IfcQ== X-Gm-Message-State: AG10YOS7GyPlEGNXcOGoVkb7n4NWZJJTqcnbNjjuQwfs7+id9GH9h05e4MtSXsxiqCFKvbXR X-Received: by 10.129.98.11 with SMTP id w11mr8811082ywb.37.1453731854868; Mon, 25 Jan 2016 06:24:14 -0800 (PST) From: Adhemerval Zanella To: libc-alpha@sourceware.org Subject: [PATCH 1/2] posix: execvpe cleanup Date: Mon, 25 Jan 2016 12:24:04 -0200 Message-Id: <1453731845-32255-1-git-send-email-adhemerval.zanella@linaro.org> This patch removes all the dynamic allocation on execvpe code and instead use direct stack allocation. This is QoI approach to make it possible use in scenarios where memory is shared with parent (vfork or clone with CLONE_VM). For default process spawn (script file without a shebang), stack allocation is bounded by NAME_MAX plus PATH_MAX plus 1. Large file arguments returns an error (ENAMETOOLONG). This differs than current GLIBC pratice in general, but it used to limit stack allocation for large inputs. Also, path in PATH environment variable larger than PATH_MAX are ignored. The shell direct execution exeception, where execve returns ENOEXEC, might requires a large stack allocation due large input argument list. Tested on i686, x86_64, powerpc64le, and aarch64. * posix/execvpe.c (__execvpe): Remove dynamic allocation. --- posix/execvpe.c | 233 +++++++++++++++++++++----------------------------------- 2 files changed, 92 insertions(+), 145 deletions(-) -- 1.9.1 diff --git a/posix/execvpe.c b/posix/execvpe.c index 61697a7..625f744 100644 --- a/posix/execvpe.c +++ b/posix/execvpe.c @@ -15,7 +15,6 @@ License along with the GNU C Library; if not, see . */ -#include #include #include #include @@ -23,22 +22,29 @@ #include #include #include - +#include /* The file is accessible but it is not an executable file. Invoke the shell to interpret it as a script. */ static void -internal_function -scripts_argv (const char *file, char *const argv[], int argc, char **new_argv) +maybe_script_execute (const char *path, char *const argv[], char *const envp[]) { + /* Count the arguments. */ + int argc = 0; + while (argv[argc++]); + /* Construct an argument list for the shell. */ + char *new_argv[argc]; new_argv[0] = (char *) _PATH_BSHELL; - new_argv[1] = (char *) file; + new_argv[1] = (char *) path; while (argc > 1) { new_argv[argc] = argv[argc - 1]; --argc; } + + /* Execute the shell. */ + __execve (new_argv[0], new_argv, envp); } @@ -47,170 +53,107 @@ scripts_argv (const char *file, char *const argv[], int argc, char **new_argv) int __execvpe (const char *file, char *const argv[], char *const envp[]) { + /* We check the simple case first. */ if (*file == '\0') { - /* We check the simple case first. */ __set_errno (ENOENT); return -1; } - if (strchr (file, '/') != NULL) + /* Don't search when it contains a slash. */ + if (strchr (file, '/')) { - /* Don't search when it contains a slash. */ __execve (file, argv, envp); if (errno == ENOEXEC) - { - /* Count the arguments. */ - int argc = 0; - while (argv[argc++]) - ; - size_t len = (argc + 1) * sizeof (char *); - char **script_argv; - void *ptr = NULL; - if (__libc_use_alloca (len)) - script_argv = alloca (len); - else - script_argv = ptr = malloc (len); - - if (script_argv != NULL) - { - scripts_argv (file, argv, argc, script_argv); - __execve (script_argv[0], script_argv, envp); - - free (ptr); - } - } + maybe_script_execute (file, argv, envp); + + return -1; } - else + + const char *path = getenv ("PATH"); + if (!path) + path = CS_PATH; + /* Although GLIBC does not enforce NAME_MAX, we set it as the maximum + size to avoid unbounded stack allocation. Same applies for + PATH_MAX. */ + size_t file_len = __strnlen (file, NAME_MAX + 1); + if (file_len > NAME_MAX) { - size_t pathlen; - size_t alloclen = 0; - char *path = getenv ("PATH"); - if (path == NULL) - { - pathlen = confstr (_CS_PATH, (char *) NULL, 0); - alloclen = pathlen + 1; - } - else - pathlen = strlen (path); + errno = ENAMETOOLONG; + return -1; + } + size_t path_len = __strnlen (path, PATH_MAX - 1) + 1; - size_t len = strlen (file) + 1; - alloclen += pathlen + len + 1; + const char *subp; + bool got_eacces = false; + for (const char *p = path; ; p = subp) + { + char buffer[path_len + file_len + 1]; - char *name; - char *path_malloc = NULL; - if (__libc_use_alloca (alloclen)) - name = alloca (alloclen); - else - { - path_malloc = name = malloc (alloclen); - if (name == NULL) - return -1; - } + subp = __strchrnul (p, ':'); - if (path == NULL) + /* PATH is larger than PATH_MAX and thus potentially larger than + the stack allocation. */ + if (subp - p >= path_len) { - /* There is no `PATH' in the environment. - The default search path is the current directory - followed by the path `confstr' returns for `_CS_PATH'. */ - path = name + pathlen + len + 1; - path[0] = ':'; - (void) confstr (_CS_PATH, path + 1, pathlen); + /* If there is only one path, bail out. */ + if (!*subp) + break; + /* Otherwise skip to next one. */ + continue; } - /* Copy the file name at the top. */ - name = (char *) memcpy (name + pathlen + 1, file, len); - /* And add the slash. */ - *--name = '/'; + /* Set current path considered plus a '/'. */ + memcpy (buffer, p, subp - p); + buffer[subp - p] = '/'; + /* And the file to execute. */ + memcpy (buffer + (subp - p) + (subp > p), file, file_len + 1); + + __execve (buffer, argv, envp); + + if (errno == ENOEXEC) + maybe_script_execute (buffer, argv, envp); - char **script_argv = NULL; - void *script_argv_malloc = NULL; - bool got_eacces = false; - char *p = path; - do + switch (errno) { - char *startp; - - path = p; - p = __strchrnul (path, ':'); - - if (p == path) - /* Two adjacent colons, or a colon at the beginning or the end - of `PATH' means to search the current directory. */ - startp = name + 1; - else - startp = (char *) memcpy (name - (p - path), path, p - path); - - /* Try to execute this name. If it works, execve will not return. */ - __execve (startp, argv, envp); - - if (errno == ENOEXEC) - { - if (script_argv == NULL) - { - /* Count the arguments. */ - int argc = 0; - while (argv[argc++]) - ; - size_t arglen = (argc + 1) * sizeof (char *); - if (__libc_use_alloca (alloclen + arglen)) - script_argv = alloca (arglen); - else - script_argv = script_argv_malloc = malloc (arglen); - if (script_argv == NULL) - { - /* A possible EACCES error is not as important as - the ENOMEM. */ - got_eacces = false; - break; - } - scripts_argv (startp, argv, argc, script_argv); - } - - __execve (script_argv[0], script_argv, envp); - } - - switch (errno) - { - case EACCES: - /* Record the we got a `Permission denied' error. If we end - up finding no executable we can use, we want to diagnose - that we did find one but were denied access. */ - got_eacces = true; - case ENOENT: - case ESTALE: - case ENOTDIR: - /* Those errors indicate the file is missing or not executable - by us, in which case we want to just try the next path - directory. */ - case ENODEV: - case ETIMEDOUT: - /* Some strange filesystems like AFS return even - stranger error numbers. They cannot reasonably mean - anything else so ignore those, too. */ - break; - - default: - /* Some other error means we found an executable file, but - something went wrong executing it; return the error to our - caller. */ - return -1; - } + case EACCES: + /* Record the we got a 'Permission denied' error. If we end + up finding no executable we can use, we want to diagnose + that we did find one but were denied access. */ + got_eacces = true; + case ENOENT: + case ESTALE: + case ENOTDIR: + /* Those errors indicate the file is missing or not executable + by us, in which case we want to just try the next path + directory. */ + case ENODEV: + case ETIMEDOUT: + /* Some strange filesystems like AFS return even + stranger error numbers. They cannot reasonably mean + anything else so ignore those, too. */ + break; + + default: + /* Some other error means we found an executable file, but + something went wrong executing it; return the error to our + caller. */ + return -1; } - while (*p++ != '\0'); - - /* We tried every element and none of them worked. */ - if (got_eacces) - /* At least one failure was due to permissions, so report that - error. */ - __set_errno (EACCES); - free (script_argv_malloc); - free (path_malloc); + if (*subp++ == '\0') + break; } - /* Return the error from the last attempt (probably ENOENT). */ + /* We tried every element and none of them worked. */ + if (got_eacces) + /* At least one failure was due to permissions, so report that + error. */ + __set_errno (EACCES); + return -1; + } + weak_alias (__execvpe, execvpe)