sh: Allow more scripts without #!
Austin Group bugs #1226 and #1250 changed the requirements for shell scripts without #! (POSIX does not specify #!; this is about the shell execution when execve(2) returns an [ENOEXEC] error). POSIX says we shall allow execution if the initial part intended to be parsed by the shell consists of characters and does not contain the NUL character. This allows concatenating a shell script (ending with exec or exit) and a binary payload. In order to reject common binary files such as PNG images, check that there is a lowercase letter or expansion before the last newline before the NUL character, in addition to the check for the newline character suggested by POSIX.
This commit is contained in:
parent
51cefda170
commit
e0f5c1387d
@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <paths.h>
|
#include <paths.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -140,6 +141,37 @@ shellexec(char **argv, char **envp, const char *path, int idx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool
|
||||||
|
isbinary(const char *data, size_t len)
|
||||||
|
{
|
||||||
|
const char *nul, *p;
|
||||||
|
bool hasletter;
|
||||||
|
|
||||||
|
nul = memchr(data, '\0', len);
|
||||||
|
if (nul == NULL)
|
||||||
|
return false;
|
||||||
|
/*
|
||||||
|
* POSIX says we shall allow execution if the initial part intended
|
||||||
|
* to be parsed by the shell consists of characters and does not
|
||||||
|
* contain the NUL character. This allows concatenating a shell
|
||||||
|
* script (ending with exec or exit) and a binary payload.
|
||||||
|
*
|
||||||
|
* In order to reject common binary files such as PNG images, check
|
||||||
|
* that there is a lowercase letter or expansion before the last
|
||||||
|
* newline before the NUL character, in addition to the check for
|
||||||
|
* the newline character suggested by POSIX.
|
||||||
|
*/
|
||||||
|
hasletter = false;
|
||||||
|
for (p = data; *p != '\0'; p++) {
|
||||||
|
if ((*p >= 'a' && *p <= 'z') || *p == '$' || *p == '`')
|
||||||
|
hasletter = true;
|
||||||
|
if (hasletter && *p == '\n')
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
tryexec(char *cmd, char **argv, char **envp)
|
tryexec(char *cmd, char **argv, char **envp)
|
||||||
{
|
{
|
||||||
@ -155,7 +187,7 @@ tryexec(char *cmd, char **argv, char **envp)
|
|||||||
if (in != -1) {
|
if (in != -1) {
|
||||||
n = pread(in, buf, sizeof buf, 0);
|
n = pread(in, buf, sizeof buf, 0);
|
||||||
close(in);
|
close(in);
|
||||||
if (n > 0 && memchr(buf, '\0', n) != NULL) {
|
if (n > 0 && isbinary(buf, n)) {
|
||||||
errno = ENOEXEC;
|
errno = ENOEXEC;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,7 @@ ${PACKAGE}FILES+= shellproc2.0
|
|||||||
${PACKAGE}FILES+= shellproc3.0
|
${PACKAGE}FILES+= shellproc3.0
|
||||||
${PACKAGE}FILES+= shellproc4.0
|
${PACKAGE}FILES+= shellproc4.0
|
||||||
${PACKAGE}FILES+= shellproc5.0
|
${PACKAGE}FILES+= shellproc5.0
|
||||||
|
${PACKAGE}FILES+= shellproc6.0
|
||||||
${PACKAGE}FILES+= subshell1.0 subshell1.0.stdout
|
${PACKAGE}FILES+= subshell1.0 subshell1.0.stdout
|
||||||
${PACKAGE}FILES+= subshell2.0
|
${PACKAGE}FILES+= subshell2.0
|
||||||
${PACKAGE}FILES+= subshell3.0
|
${PACKAGE}FILES+= subshell3.0
|
||||||
|
8
bin/sh/tests/execution/shellproc6.0
Normal file
8
bin/sh/tests/execution/shellproc6.0
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# $FreeBSD$
|
||||||
|
|
||||||
|
T=`mktemp -d "${TMPDIR:-/tmp}/sh-test.XXXXXXXX"` || exit
|
||||||
|
trap 'rm -rf "${T}"' 0
|
||||||
|
printf 'printf "this "\necho is a test\nexit\n\0' >"$T/testshellproc"
|
||||||
|
chmod 755 "$T/testshellproc"
|
||||||
|
PATH=$T:$PATH
|
||||||
|
[ "`testshellproc`" = "this is a test" ]
|
Loading…
x
Reference in New Issue
Block a user