@@ -39,8 +39,7 @@ Deprecated and removed features, and other changes affecting compatibility:
* The deprecated <sys/vtimes.h> header and the function vtimes have been
removed. To support old binaries, the vtimes function continues to exist
- as a compatibility symbol. Applications should use the getrlimit or
- prlimit.
+ as a compatibility symbol. Applications should use getrlimit or prlimit.
* Following a change in the tzdata 2018a release upstream, the zdump
program is now installed in the /usr/bin subdirectory. Previously,
@@ -53,6 +52,16 @@ Deprecated and removed features, and other changes affecting compatibility:
libraries that use this type in their interfaces. The new definition
improves consistency with compiler behavior in many scenarios.
+* When direct execution of a program fails with ENOEXEC, the functions
+ execlp, execvp, and execvpe will not attempt to run it as a shell
+ script anymore. The same change has been made to the compatibility
+ versions of posix_spawn and posix_spawnp (symbol version GLIBC_2_2).
+ This is an intentional deviation from POSIX, anticipating the change
+ requested in Austin Group bug #1435; the old behavior could cause
+ severe malfunctions of correct-looking code, and theoretically could
+ open security holes. Also, it was inconsistent with the behavior of
+ the rest of the exec* family of functions.
+
Changes to build and runtime requirements:
* On Linux, the system administrator needs to configure /dev/pts with
@@ -83,8 +83,6 @@ libc_hidden_proto (__getcwd)
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
@@ -88,8 +88,8 @@ tests := test-errno tstgetopt testfnm runtests runptests \
tst-execvp1 tst-execvp2 tst-execlp1 tst-execlp2 \
tst-execv1 tst-execv2 tst-execl1 tst-execl2 \
tst-execve1 tst-execve2 tst-execle1 tst-execle2 \
- tst-execvp3 tst-execvp4 \
- tst-execvpe1 tst-execvpe2 tst-execvpe3 tst-execvpe4 \
+ tst-execvp4 \
+ tst-execvpe1 tst-execvpe2 tst-execvpe4 \
tst-execvpe5 tst-execvpe6 \
tst-getaddrinfo3 tst-fnmatch2 tst-cpucount tst-cpuset \
bug-getopt1 bug-getopt2 bug-getopt3 bug-getopt4 \
@@ -105,7 +105,7 @@ tests := test-errno tstgetopt testfnm runtests runptests \
tst-wordexp-nocmd
tests-internal := bug-regex5 bug-regex20 bug-regex33 \
tst-rfc3484 tst-rfc3484-2 tst-rfc3484-3 \
- tst-glob_lstat_compat tst-spawn4-compat
+ tst-glob_lstat_compat
tests-container := bug-ga2
xtests := tst-getaddrinfo4 tst-getaddrinfo5
ifeq (yes,$(build-shared))
@@ -33,43 +33,10 @@
# endif
#endif
-/* The file is accessible but it is not an executable file. Invoke
- the shell to interpret it as a script. */
-static void
-maybe_script_execute (const char *file, char *const argv[], char *const envp[])
-{
- ptrdiff_t argc;
- for (argc = 0; argv[argc] != NULL; argc++)
- {
- if (argc == INT_MAX - 1)
- {
- errno = E2BIG;
- return;
- }
- }
-
- /* Construct an argument list for the shell based on original arguments:
- 1. Empty list (argv = { NULL }, argc = 1 }: new argv will contain 3
- arguments - default shell, script to execute, and ending NULL.
- 2. Non empty argument list (argc = { ..., NULL }, argc > 1}: new argv
- will contain also the default shell and the script to execute. It
- will also skip the script name in arguments and only copy script
- arguments. */
- char *new_argv[argc > 1 ? 2 + argc : 3];
- new_argv[0] = (char *) _PATH_BSHELL;
- new_argv[1] = (char *) file;
- if (argc > 1)
- memcpy (new_argv + 2, argv + 1, argc * sizeof (char *));
- else
- new_argv[2] = NULL;
-
- /* Execute the shell. */
- __execve (new_argv[0], new_argv, envp);
-}
-
-static int
-__execvpe_common (const char *file, char *const argv[], char *const envp[],
- bool exec_script)
+/* 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[])
{
/* We check the simple case first. */
if (*file == '\0')
@@ -82,10 +49,6 @@ __execvpe_common (const char *file, char *const argv[], char *const envp[],
if (strchr (file, '/') != NULL)
{
__execve (file, argv, envp);
-
- if (errno == ENOEXEC && exec_script)
- maybe_script_execute (file, argv, envp);
-
return -1;
}
@@ -135,13 +98,6 @@ __execvpe_common (const char *file, char *const argv[], char *const envp[],
__execve (buffer, argv, envp);
- 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
- behaves as O(P+C) instead. */
- maybe_script_execute (buffer, argv, envp);
-
switch (errno)
{
case EACCES:
@@ -181,19 +137,4 @@ __execvpe_common (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 execute NOEXEC files. */
-int
-__execvpex (const char *file, char *const argv[], char *const envp[])
-{
- return __execvpe_common (file, argv, envp, false);
-}
@@ -32,17 +32,9 @@ __posix_spawn (pid_t *pid, const char *path,
versioned_symbol (libc, __posix_spawn, posix_spawn, GLIBC_2_15);
libc_hidden_def (__posix_spawn)
-
+/* There is no longer any behavior difference between the GLIBC_2_2
+ and GLIBC_2_15 versions of __posix_spawn, but we have to keep both
+ symbols around for compatibility. */
#if SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_15)
-int
-attribute_compat_text_section
-__posix_spawn_compat (pid_t *pid, const char *file,
- const posix_spawn_file_actions_t *file_actions,
- const posix_spawnattr_t *attrp, char *const argv[],
- char *const envp[])
-{
- return __spawni (pid, file, file_actions, attrp, argv, envp,
- SPAWN_XFLAGS_TRY_SHELL);
-}
-compat_symbol (libc, __posix_spawn_compat, posix_spawn, GLIBC_2_2);
+compat_symbol (libc, __posix_spawn, posix_spawn, GLIBC_2_2);
#endif
@@ -64,7 +64,6 @@ struct __spawn_action
};
#define SPAWN_XFLAGS_USE_PATH 0x1
-#define SPAWN_XFLAGS_TRY_SHELL 0x2
extern int __posix_spawn_file_actions_realloc (posix_spawn_file_actions_t *
file_actions)
@@ -32,17 +32,9 @@ __posix_spawnp (pid_t *pid, const char *file,
}
versioned_symbol (libc, __posix_spawnp, posix_spawnp, GLIBC_2_15);
-
+/* There is no longer any behavior difference between the GLIBC_2_2
+ and GLIBC_2_15 versions of __posix_spawnp, but we have to keep both
+ symbols around for compatibility. */
#if SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_15)
-int
-attribute_compat_text_section
-__posix_spawnp_compat (pid_t *pid, const char *file,
- const posix_spawn_file_actions_t *file_actions,
- const posix_spawnattr_t *attrp, char *const argv[],
- char *const envp[])
-{
- return __spawni (pid, file, file_actions, attrp, argv, envp,
- SPAWN_XFLAGS_USE_PATH | SPAWN_XFLAGS_TRY_SHELL);
-}
-compat_symbol (libc, __posix_spawnp_compat, posix_spawnp, GLIBC_2_2);
+compat_symbol (libc, __posix_spawnp, posix_spawnp, GLIBC_2_2);
#endif
deleted file mode 100644
@@ -1,45 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/stat.h>
-
-
-static void do_prepare (void);
-#define PREPARE(argc, argv) do_prepare ()
-static int do_test (void);
-#define TEST_FUNCTION do_test ()
-
-#include "../test-skeleton.c"
-
-#ifndef EXECVP
-# define EXECVP(file, argv) execvp (file, argv)
-#endif
-
-static char *fname;
-
-static void
-do_prepare (void)
-{
- int fd = create_temp_file ("testscript", &fname);
- dprintf (fd, "echo foo\n");
- fchmod (fd, 0700);
- close (fd);
-}
-
-
-static int
-do_test (void)
-{
- if (setenv ("PATH", test_dir, 1) != 0)
- {
- puts ("setenv failed");
- return 1;
- }
-
- char *argv[] = { fname, NULL };
- EXECVP (basename (fname), argv);
-
- /* If we come here, the execvp call failed. */
- return 1;
-}
deleted file mode 100644
@@ -1,20 +0,0 @@
-/* Check script execution without shebang for execvpe.
- Copyright (C) 2016-2020 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
- <https://www.gnu.org/licenses/>. */
-
-#define EXECVP(file, argv) execvpe (file, argv, NULL)
-#include <posix/tst-execvp3.c>
@@ -41,12 +41,12 @@ do_prepare (void)
close (logfd);
int fd1 = create_temp_file ("testscript", &fname1);
- dprintf (fd1, "echo foo $1 $2 $3 > %s\n", logname);
+ dprintf (fd1, "#! /bin/sh\necho foo $1 $2 $3 > %s\n", logname);
fchmod (fd1, 0700);
close (fd1);
int fd2 = create_temp_file ("testscript", &fname2);
- dprintf (fd2, "echo foo > %s\n", logname);
+ dprintf (fd2, "#! /bin/sh\necho foo > %s\n", logname);
fchmod (fd2, 0700);
close (fd2);
}
deleted file mode 100644
@@ -1,77 +0,0 @@
-/* Check if posix_spawn does handle correctly ENOEXEC files.
- Copyright (C) 2018-2020 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
- <https://www.gnu.org/licenses/>. */
-
-#include <spawn.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-
-#include <support/xunistd.h>
-#include <support/check.h>
-#include <support/temp_file.h>
-
-#include <shlib-compat.h>
-#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);
-
- const char script[] = "exit 65";
- xwrite (fd, script, sizeof (script) - 1);
- xclose (fd);
-
- 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) == 65);
-
- 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) == 65);
-
- return 0;
-}
-#else
-static int
-do_test (void)
-{
- return 77;
-}
-#endif
-
-#include <support/test-driver.c>
@@ -90,9 +90,6 @@ do_test (void)
char *argv1[] = { (char *) "script1.sh", (char *) "1", NULL };
run_script ("script1.sh", argv1);
- char *argv2[] = { (char *) "script2.sh", (char *) "2", NULL };
- run_script ("script2.sh", argv2);
-
/* Same as before but with execlp. */
for (size_t i = 0; i < 5; i++)
{
@@ -104,7 +101,7 @@ do_test (void)
}
else if (pid == 0)
{
- execlp ("script2.sh", "script2.sh", "3", NULL);
+ execlp ("script1.sh", "script1.sh", "2", NULL);
_exit (errno);
}
int status;
@@ -121,8 +118,8 @@ do_test (void)
}
unsetenv ("PATH");
- char *argv4[] = { (char *) "echo", (char *) "script 4", NULL };
- run_script ("echo", argv4);
+ char *argv2[] = { (char *) "echo", (char *) "script 3", NULL };
+ run_script ("echo", argv2);
return 0;
}
@@ -153,23 +150,17 @@ do_prepare (void)
char script0[len + sizeof "/script0.sh"];
char script1[len + sizeof "/script1.sh"];
- char script2[len + sizeof "/script2.sh"];
strcpy (stpcpy (script0, tmpdirname), "/script0.sh");
strcpy (stpcpy (script1, tmpdirname), "/script1.sh");
- strcpy (stpcpy (script2, tmpdirname), "/script2.sh");
add_temp_file (tmpdirname);
add_temp_file (script0);
add_temp_file (script1);
- add_temp_file (script2);
const char content0[] = "#!/bin/sh\necho empty\n";
create_script (script0, content0, sizeof content0);
const char content1[] = "#!/bin/sh\necho script $1\n";
create_script (script1, content1, sizeof content1);
-
- const char content2[] = "echo script $1\n";
- create_script (script2, content2, sizeof content2);
}
@@ -27,7 +27,6 @@
#include <sys/mman.h>
#include <not-cancel.h>
#include <local-setxid.h>
-#include <shlib-compat.h>
#include <nptl/pthreadP.h>
#include <dl-sysdep.h>
#include <libc-pointer-arith.h>
@@ -56,31 +55,6 @@ struct posix_spawn_args
int pipe[2];
};
-/* Older version requires that shell script without shebang definition
- to be called explicitly using /bin/sh (_PATH_BSHELL). */
-static void
-maybe_script_execute (struct posix_spawn_args *args)
-{
- if (SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_15)
- && (args->xflags & SPAWN_XFLAGS_TRY_SHELL) && errno == ENOEXEC)
- {
- char *const *argv = args->argv;
- ptrdiff_t argc = args->argc;
-
- /* Construct an argument list for the shell. */
- char *new_argv[argc + 2];
- new_argv[0] = (char *) _PATH_BSHELL;
- new_argv[1] = (char *) args->file;
- if (argc > 1)
- memcpy (new_argv + 2, argv + 1, argc * sizeof (char *));
- else
- new_argv[2] = NULL;
-
- /* Execute the shell. */
- args->exec (new_argv[0], new_argv, args->envp);
- }
-}
-
/* Function used in the clone call to setup the signals mask, posix_spawn
attributes, and file actions. */
static int
@@ -174,12 +148,13 @@ __spawni_child (void *arguments)
case spawn_do_open:
{
- /* POSIX states that if fildes was already an open file descriptor,
- it shall be closed before the new file is opened. This avoid
- pontential issues when posix_spawn plus addopen action is called
- with the process already at maximum number of file descriptor
- opened and also for multiple actions on single-open special
- paths (like /dev/watchdog). */
+ /* If fildes was already an open file descriptor,
+ POSIX specifies we should close it before opening
+ the new file. This avoids potential issues when
+ posix_spawn is called with the process already
+ close to its maximum number of open file
+ descriptors, and also with special paths that can
+ only be opened once (like /dev/watchdog). */
__close_nocancel (action->action.open_action.fd);
int new_fd = __open_nocancel (action->action.open_action.path,
@@ -242,11 +217,6 @@ __spawni_child (void *arguments)
args->exec (args->file, args->argv, args->envp);
- /* This is compatibility function required to enable posix_spawn run
- script without shebang definition for older posix_spawn versions
- (2.15). */
- maybe_script_execute (args);
-
fail:
/* errno should have an appropriate non-zero value; otherwise,
there's a bug in glibc or the kernel. For lack of an error code
@@ -332,8 +302,6 @@ __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 ? __execvpex : __execve);
+ xflags & SPAWN_XFLAGS_USE_PATH ? __execvpe : __execve);
}
@@ -26,7 +26,6 @@
#include <sys/mman.h>
#include <not-cancel.h>
#include <local-setxid.h>
-#include <shlib-compat.h>
#include <nptl/pthreadP.h>
#include <dl-sysdep.h>
#include <libc-pointer-arith.h>
@@ -89,31 +88,6 @@ struct posix_spawn_args
int err;
};
-/* Older version requires that shell script without shebang definition
- to be called explicitly using /bin/sh (_PATH_BSHELL). */
-static void
-maybe_script_execute (struct posix_spawn_args *args)
-{
- if (SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_15)
- && (args->xflags & SPAWN_XFLAGS_TRY_SHELL) && errno == ENOEXEC)
- {
- char *const *argv = args->argv;
- ptrdiff_t argc = args->argc;
-
- /* Construct an argument list for the shell. */
- char *new_argv[argc + 2];
- new_argv[0] = (char *) _PATH_BSHELL;
- new_argv[1] = (char *) args->file;
- if (argc > 1)
- memcpy (new_argv + 2, argv + 1, argc * sizeof (char *));
- else
- new_argv[2] = NULL;
-
- /* Execute the shell. */
- args->exec (new_argv[0], new_argv, args->envp);
- }
-}
-
/* Function used in the clone call to setup the signals mask, posix_spawn
attributes, and file actions. It run on its own stack (provided by the
posix_spawn call). */
@@ -221,12 +195,13 @@ __spawni_child (void *arguments)
case spawn_do_open:
{
- /* POSIX states that if fildes was already an open file descriptor,
- it shall be closed before the new file is opened. This avoid
- pontential issues when posix_spawn plus addopen action is called
- with the process already at maximum number of file descriptor
- opened and also for multiple actions on single-open special
- paths (like /dev/watchdog). */
+ /* If fildes was already an open file descriptor,
+ POSIX specifies we should close it before opening
+ the new file. This avoids potential issues when
+ posix_spawn is called with the process already
+ close to its maximum number of open file
+ descriptors, and also with special paths that can
+ only be opened once (like /dev/watchdog). */
__close_nocancel (action->action.open_action.fd);
int ret = __open_nocancel (action->action.open_action.path,
@@ -291,11 +266,6 @@ __spawni_child (void *arguments)
args->exec (args->file, args->argv, args->envp);
- /* This is compatibility function required to enable posix_spawn run
- script without shebang definition for older posix_spawn versions
- (2.15). */
- maybe_script_execute (args);
-
fail:
/* errno should have an appropriate non-zero value; otherwise,
there's a bug in glibc or the kernel. For lack of an error code
@@ -426,8 +396,6 @@ __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 ? __execvpex :__execve);
+ xflags & SPAWN_XFLAGS_USE_PATH ? __execvpe : __execve);
}