Skip to content

Commit

Permalink
Allow more scripts without #!
Browse files Browse the repository at this point in the history
This change modifies the zsh binary safety check surrounding execve() so
it can run shell scripts having concatenated binary content. We're using
the same safety check as FreeBSD /bin/sh [1]. POSIX was recently revised
to require this behavior:

    "The input file may be of any type, but the initial portion of the
     file intended to be parsed according to the shell grammar (XREF to
     XSH 2.10.2 Shell Grammar Rules) shall consist of characters and
     shall not contain the NUL character. The shell shall not enforce
     any line length limits."

    "Earlier versions of this standard required that input files to the
     shell be text files except that line lengths were unlimited.
     However, that was overly restrictive in relation to the fact that
     shells can parse a script without a trailing newline, and in
     relation to a common practice of concatenating a shell script
     ending with an 'exit' or 'exec $command' with a binary data payload
     to form a single-file self-extracting archive." [2] [3]

One example use case of such scripts, is the Cosmopolitan C Library [4]
which configuse the GNU Linker to output a polyglot shell+binary format
that runs on Linux / Mac / Windows / FreeBSD / OpenBSD.

[1] freebsd/freebsd-src@9a1cd36
[2] http://austingroupbugs.net/view.php?id=1250
[3] http://austingroupbugs.net/view.php?id=1226#c4394
[4] https://justine.lol/cosmopolitan/index.html
  • Loading branch information
jart committed Jan 26, 2021
1 parent 07765d5 commit 94a4bc1
Showing 1 changed file with 23 additions and 4 deletions.
27 changes: 23 additions & 4 deletions Src/exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -547,10 +547,29 @@ zexecve(char *pth, char **argv, char **newenvp)
}
}
} else if (eno == ENOEXEC) {
for (t0 = 0; t0 != ct; t0++)
if (!execvebuf[t0])
break;
if (t0 == ct) {
/* Perform binary safety check on classic shell *
* scripts (shebang wasn't introduced until UNIX *
* Seventh Edition). POSIX says we shall allow *
* execution of scripts with concatenated binary *
* and suggests checking a line exists before the *
* first NUL character with a lowercase letter or *
* expansion. This is consistent with FreeBSD sh. */
int isbinary, hasletter;
if (!(ptr2 = memchr(execvebuf, '\0', ct))) {
isbinary = 0;
} else {
isbinary = 1;
hasletter = 0;
for (ptr = execvebuf; ptr < ptr2; ptr++) {
if (islower(*ptr) || *ptr == '$' || *ptr == '`')
hasletter = 1;
if (hasletter && *ptr == '\n') {
isbinary = 0;
break;
}
}
}
if (!isbinary) {
argv[-1] = "sh";
winch_unblock();
execve("/bin/sh", argv - 1, newenvp);
Expand Down

0 comments on commit 94a4bc1

Please sign in to comment.