zsh-workers
 help / color / mirror / code / Atom feed
* PATCH: Allow more scripts without #!
@ 2021-01-26  6:35 Justine Tunney
  2021-02-15 15:45 ` Daniel Shahaf
  0 siblings, 1 reply; 6+ messages in thread
From: Justine Tunney @ 2021-01-26  6:35 UTC (permalink / raw)
  To: zsh-workers


[-- Attachment #1.1: Type: text/plain, Size: 698 bytes --]

This change updates the binary safety check so that zsh can run shell
scripts with concatenated binary content within the first 128 bytes,
provided a line exists beforehand with a lowercase character or shell
expansion syntax. Note that this only impacts classic implicit shell
scripts which don't have a shebang line.

POSIX rules were updated to loosen binary restrictions and require this
behavior going forward. A similar change was made last year to the FreeBSD
Almquist Shell. It's needed by projects such as the Cosmopolitan C Library,
which creates polyglot executables that run on all operating systems.

I release this change into the public domain. See unlicense / creative
commons cc0.

[-- Attachment #1.2: Type: text/html, Size: 777 bytes --]

[-- Attachment #2: execve.patch --]
[-- Type: application/octet-stream, Size: 3388 bytes --]

commit 94a4bc14bb2e415ec3d10cf716512bd3e0d99f48
Author: Justine Tunney <jtunney@gmail.com>
Date:   Mon Jan 25 21:34:50 2021 -0800

    Allow more scripts without #!
    
    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] https://github.com/freebsd/freebsd-src/commit/9a1cd363318b7e9e70ef6af27d1675b371c16b1a
    [2] http://austingroupbugs.net/view.php?id=1250
    [3] http://austingroupbugs.net/view.php?id=1226#c4394
    [4] https://justine.lol/cosmopolitan/index.html

diff --git a/Src/exec.c b/Src/exec.c
index ecad923de..2301f85ad 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -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);

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2021-02-17 14:54 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-26  6:35 PATCH: Allow more scripts without #! Justine Tunney
2021-02-15 15:45 ` Daniel Shahaf
2021-02-15 22:44   ` Bart Schaefer
2021-02-15 23:06     ` Justine Tunney
2021-02-16  9:18     ` Peter Stephenson
2021-02-17 14:53       ` Justine Tunney

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).