From patchwork Wed Jun 6 18:11:51 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adhemerval Zanella Netto X-Patchwork-Id: 925993 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=sourceware.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=libc-alpha-return-92944-incoming=patchwork.ozlabs.org@sourceware.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=linaro.org Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=sourceware.org header.i=@sourceware.org header.b="JImksP7h"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 411GwW3Bmhz9s01 for ; Thu, 7 Jun 2018 04:12:11 +1000 (AEST) 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=EZY5kfmKfYmSZH7mX8t1woIjTEUiIeEDEf6pKthzHIQKY7RyKaSkp UX3fB8asVa2KP4ogmi6uwYie3OY7+C6Hx4QDGpNjQ9mud3Z0cidVMtenONrjnv+w CgUSZ1AAvjS+QTNRkVySuawco5mfNkWB40FprZGslgMTjeV74PyZDY= 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=w4OAWC6K6Z55bOQbgAMcCkbdiZQ=; b=JImksP7hfP3F5QDAMKqMyNGvQL61 JBTsydPfWNKr2PJuBf2+LGjinvZMN6IIcY/qZU6OvnSmpvde6n8bhV6SFQTL/W+7 rNMfUl8+lixnEHwCZcrFprqa0FTvBGDAR6K8LNzSWmsQTh7WF8sMq1CWlrD1/MRB 3VbVumf2SpNgK5E= Received: (qmail 54792 invoked by alias); 6 Jun 2018 18:12:05 -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 54065 invoked by uid 89); 6 Jun 2018 18:12:04 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-25.5 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=ham version=3.3.2 spammy=faulty X-HELO: mail-qt0-f170.google.com X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id; bh=PL0dr+KBNd5buOd2OogUHrVnpNcI5Sdb0RCcl8IEOT0=; b=dSLKO5RqcMwIDceiN+KU23/PVOfmo8TzVWb+C9JtZJLBCOthiqQGO5nQ0owIqshZGL mRy0Nq+EOvFEtEr5CZIafzzNFnzUOjYUH752m3JTP3DXU66jQuLLUY8m8rMqxur365I0 2RlAZbd0tpQCEndpiANM2/ZiyAacll2fgi4mGettb1Uo5YxPne2sBKblTonJwqE+P2Yd AQBE0VZhFdGpnv/vaGB0e/2maTOT9pw2Gs0IuPTCksEZHlL2jmQ4V0Hj4KVJUIZaEZWE rfO7ZD8EmKNhVbLtHtiNnprZA8teW7C1H1B9gIX26+zcDA7chl/BOLH4qCWAoCgyNxhw TNOQ== X-Gm-Message-State: APt69E3jF6zr//iv8O91fc2O2bPoHFATyAqbUXyLKkya/yhKqU4lil4U yWITbvO1Aw/kTPhUzuPsog+t/j4IgXQ= X-Google-Smtp-Source: ADUXVKKip+Zp8Rz+jz3YwlGeomY4E4pGC/rfSR4h+P0k/xa3gGjX5oD2sfrWYAEUQCC+leCZE1t4Yg== X-Received: by 2002:a0c:b5af:: with SMTP id g47-v6mr3761735qve.94.1528308719778; Wed, 06 Jun 2018 11:11:59 -0700 (PDT) From: Adhemerval Zanella To: libc-alpha@sourceware.org Subject: [PATCH] posix: Fix posix_spawnpi to not execute invalid binaries in non compat mode (BZ#23264) Date: Wed, 6 Jun 2018 15:11:51 -0300 Message-Id: <1528308711-25964-1-git-send-email-adhemerval.zanella@linaro.org> Current posix_spawnp implementation wrongly tries to execute invalid binaries (for instance script without shebang) as a shell script in non compat mode. It was a regression introduced by 9ff72da471a509a8c19791efe469f47fa6977410 when __spawni started to use __execvpe instead of __execve (glibc __execvpe try to execute ENOEXEC as shell script regardless). This patch fixes it by using an internal symbol (__execvpex) with the faulty semantic (since compat mode is handled by spawni.c itself). It was reported by Daniel Drake on libc-help [1]. Checked on x86_64-linux-gnu and i686-linux-gnu. [BZ #23264] * include/unistd.h (__execvpex): New prototype. * posix/Makefile (tests): Add tst-spawn4. (tests-internal): Add tst-spawn4-compat. * posix/execvpe.c (__execvpe_common, __execvpex): New functions. * posix/tst-spawn4-compat.c: New file. * posix/tst-spawn4.c: Likewise. * sysdeps/unix/sysv/linux/spawni.c (__spawni): Do not interpret invalid binaries as shell scripts. * sysdeps/posix/spawni.c (__spawni): Likewise. [1] https://sourceware.org/ml/libc-help/2018-06/msg00012.html --- ChangeLog | 13 ++++++ include/unistd.h | 2 + posix/Makefile | 4 +- posix/execvpe.c | 26 ++++++++---- posix/tst-spawn4-compat.c | 85 ++++++++++++++++++++++++++++++++++++++++ posix/tst-spawn4.c | 56 ++++++++++++++++++++++++++ sysdeps/posix/spawni.c | 4 +- sysdeps/unix/sysv/linux/spawni.c | 4 +- 8 files changed, 183 insertions(+), 11 deletions(-) create mode 100644 posix/tst-spawn4-compat.c create mode 100644 posix/tst-spawn4.c diff --git a/include/unistd.h b/include/unistd.h index 0f91b8b..a171b00 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -77,6 +77,8 @@ extern char *__getcwd (char *__buf, size_t __size) attribute_hidden; extern int __rmdir (const char *__path) attribute_hidden; extern int __execvpe (const char *file, char *const argv[], char *const envp[]) attribute_hidden; +extern int __execvpex (const char *file, char *const argv[], + char *const envp[]) attribute_hidden; /* Get the canonical absolute name of the named directory, and put it in SIZE bytes of BUF. Returns NULL if the directory couldn't be determined or diff --git a/posix/Makefile b/posix/Makefile index e9730ee..4a9a1b5 100644 --- a/posix/Makefile +++ b/posix/Makefile @@ -95,10 +95,10 @@ tests := test-errno tstgetopt testfnm runtests runptests \ tst-posix_spawn-fd tst-posix_spawn-setsid \ tst-posix_fadvise tst-posix_fadvise64 \ tst-sysconf-empty-chroot tst-glob_symlinks tst-fexecve \ - tst-glob-tilde test-ssize-max + tst-glob-tilde test-ssize-max tst-spawn4 tests-internal := bug-regex5 bug-regex20 bug-regex33 \ tst-rfc3484 tst-rfc3484-2 tst-rfc3484-3 \ - tst-glob_lstat_compat + tst-glob_lstat_compat tst-spawn4-compat xtests := bug-ga2 tst-getaddrinfo4 tst-getaddrinfo5 ifeq (yes,$(build-shared)) test-srcs := globtest diff --git a/posix/execvpe.c b/posix/execvpe.c index 859c0f6..8de9c14 100644 --- a/posix/execvpe.c +++ b/posix/execvpe.c @@ -67,11 +67,9 @@ maybe_script_execute (const char *file, char *const argv[], char *const envp[]) __execve (new_argv[0], new_argv, envp); } - -/* Execute FILE, searching in the `PATH' environment variable if it contains - no slashes, with arguments ARGV and environment from ENVP. */ -int -__execvpe (const char *file, char *const argv[], char *const envp[]) +static int +__execvpe_common (const char *file, char *const argv[], char *const envp[], + bool exec_script) { /* We check the simple case first. */ if (*file == '\0') @@ -85,7 +83,7 @@ __execvpe (const char *file, char *const argv[], char *const envp[]) { __execve (file, argv, envp); - if (errno == ENOEXEC) + if (errno == ENOEXEC && exec_script) maybe_script_execute (file, argv, envp); return -1; @@ -137,7 +135,7 @@ __execvpe (const char *file, char *const argv[], char *const envp[]) __execve (buffer, argv, envp); - if (errno == ENOEXEC) + if (errno == ENOEXEC && exec_script) /* This has O(P*C) behavior, where P is the length of the path and C is the argument count. A better strategy would be allocate the substitute argv and reuse it each time through the loop (so it @@ -184,4 +182,18 @@ __execvpe (const char *file, char *const argv[], char *const envp[]) return -1; } +/* Execute FILE, searching in the `PATH' environment variable if it contains + no slashes, with arguments ARGV and environment from ENVP. */ +int +__execvpe (const char *file, char *const argv[], char *const envp[]) +{ + return __execvpe_common (file, argv, envp, true); +} weak_alias (__execvpe, execvpe) + +/* Same as __EXECVPE, but does not try to issue ENOEXEC files. */ +int +__execvpex (const char *file, char *const argv[], char *const envp[]) +{ + return __execvpe_common (file, argv, envp, false); +} diff --git a/posix/tst-spawn4-compat.c b/posix/tst-spawn4-compat.c new file mode 100644 index 0000000..7b2b07a --- /dev/null +++ b/posix/tst-spawn4-compat.c @@ -0,0 +1,85 @@ +/* Check if posix_spawn does handle correctly ENOEXEC files. + Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#if TEST_COMPAT (libc, GLIBC_2_0, GLIBC_2_15) + +compat_symbol_reference (libc, posix_spawn, posix_spawn, GLIBC_2_2); +compat_symbol_reference (libc, posix_spawnp, posix_spawnp, GLIBC_2_2); + +static int +do_test (void) +{ + char *scriptname; + int fd = create_temp_file ("tst-spawn4.", &scriptname); + TEST_VERIFY_EXIT (fd >= 0); + +#define EXPECTED_EXIT_CODE 65 +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + + const char script[] = "exit " STR(EXPECTED_EXIT_CODE); + write (fd, script, sizeof (script) - 1); + + TEST_VERIFY_EXIT (close (fd) == 0); + + TEST_VERIFY_EXIT (chmod (scriptname, 0x775) == 0); + + pid_t pid; + int status; + + /* For compat symbol it verifies that trying to issued a shell script + without a shebang is correctly executed. */ + status = posix_spawn (&pid, scriptname, NULL, NULL, (char *[]) { 0 }, + (char *[]) { 0 }); + TEST_VERIFY_EXIT (status == 0); + + TEST_VERIFY_EXIT (waitpid (pid, &status, 0) == pid); + TEST_VERIFY_EXIT (WIFEXITED (status) == 1 + && WEXITSTATUS (status) == EXPECTED_EXIT_CODE); + + status = posix_spawnp (&pid, scriptname, NULL, NULL, (char *[]) { 0 }, + (char *[]) { 0 }); + TEST_VERIFY_EXIT (status == 0); + + TEST_VERIFY_EXIT (waitpid (pid, &status, 0) == pid); + TEST_VERIFY_EXIT (WIFEXITED (status) == 1 + && WEXITSTATUS (status) == EXPECTED_EXIT_CODE); + + return 0; +} +#else +static int +do_test (void) +{ + return 77; +} +#endif + +#include diff --git a/posix/tst-spawn4.c b/posix/tst-spawn4.c new file mode 100644 index 0000000..f1a7858 --- /dev/null +++ b/posix/tst-spawn4.c @@ -0,0 +1,56 @@ +/* Check if posix_spawn does handle correctly ENOEXEC files. + Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include + +#include +#include + +static int +do_test (void) +{ + char *scriptname; + int fd = create_temp_file ("tst-spawn4.", &scriptname); + TEST_VERIFY_EXIT (fd >= 0); + + const char script[] = "echo it should not happen"; + write (fd, script, sizeof (script) - 1); + + TEST_VERIFY_EXIT (close (fd) == 0); + + TEST_VERIFY_EXIT (chmod (scriptname, 0x775) == 0); + + pid_t pid; + int status; + + /* Check if scripts without shebang are correctly not executed. */ + status = posix_spawn (&pid, scriptname, NULL, NULL, (char *[]) { 0 }, + (char *[]) { 0 }); + TEST_VERIFY_EXIT (status == ENOEXEC); + + status = posix_spawnp (&pid, scriptname, NULL, NULL, (char *[]) { 0 }, + (char *[]) { 0 }); + TEST_VERIFY_EXIT (status == ENOEXEC); + + return 0; +} + +#include diff --git a/sysdeps/posix/spawni.c b/sysdeps/posix/spawni.c index 36bb5b4..b138ab4 100644 --- a/sysdeps/posix/spawni.c +++ b/sysdeps/posix/spawni.c @@ -310,6 +310,8 @@ __spawni (pid_t * pid, const char *file, const posix_spawnattr_t * attrp, char *const argv[], char *const envp[], int xflags) { + /* It uses __execvpex to avoid run ENOEXEC in non compatibility mode (it + will be handled by maybe_script_execute). */ return __spawnix (pid, file, acts, attrp, argv, envp, xflags, - xflags & SPAWN_XFLAGS_USE_PATH ? __execvpe : __execve); + xflags & SPAWN_XFLAGS_USE_PATH ? __execvpex : __execve); } diff --git a/sysdeps/unix/sysv/linux/spawni.c b/sysdeps/unix/sysv/linux/spawni.c index 0391b9b..cf0213e 100644 --- a/sysdeps/unix/sysv/linux/spawni.c +++ b/sysdeps/unix/sysv/linux/spawni.c @@ -404,6 +404,8 @@ __spawni (pid_t * pid, const char *file, const posix_spawnattr_t * attrp, char *const argv[], char *const envp[], int xflags) { + /* It uses __execvpex to avoid run ENOEXEC in non compatibility mode (it + will be handled by maybe_script_execute). */ return __spawnix (pid, file, acts, attrp, argv, envp, xflags, - xflags & SPAWN_XFLAGS_USE_PATH ? __execvpe : __execve); + xflags & SPAWN_XFLAGS_USE_PATH ? __execvpex :__execve); }