Dürfen Shells NUL-Bytes in Skripten ignorieren?

Dürfen Shells NUL-Bytes in Skripten ignorieren?

Weil es genau das ist, was einige von ihnen tun.

> echo echo Hallo, Baby! | iconv -f utf-8 -t utf-16le > /tmp/hallo
> chmod 755 /tmp/hallo
> dash /tmp/hallo
Hallo, Baby!
> bash /tmp/hallo
/tmp/hallo: /tmp/hallo: cannot execute binary file
> (echo '#'; echo echo Hallo, Baby! | iconv -f utf-8 -t utf-16le) > /tmp/hallo
> bash /tmp/hallo
Hallo, Baby!
> mksh /tmp/hallo
Hallo, Baby!
> cat -v /tmp/hallo
#
e^@c^@h^@o^@ ^@H^@a^@l^@l^@o^@,^@ ^@B^@a^@b^@y^@!^@
^@

Ist das tatsächlich ein Kompatibilitätsproblem?erforderlichnach dem Standard? Weil es ziemlich gefährlich und unerwartet aussieht.

Antwort1

GemäßPOSIX,

Die Eingabedatei muss eine Textdatei sein, mit der Ausnahme, dass die Zeilenlänge unbegrenzt sein muss.¹

NUL-Zeichen² in der EingabeMachen Sie es zu einem Nicht-Text, daher ist das Verhalten für POSIX nicht spezifiziert, sodass shImplementierungen tun können, was sie wollen (und ein POSIX-kompatiblesSkriptdarf keine NULs enthalten).

Einige Shells durchsuchen die ersten Bytes nach Nullen und weigern sich, das Skript auszuführen, da sie davon ausgehen, dass Sie versehentlich versucht haben, eine Datei auszuführen, die kein Skript ist.

Das ist nützlich, weil die exec*p()Funktionen, envBefehle sh, find -exec...erforderlichum eine Shell aufzurufen, die einen Befehl interpretiert, wenn das System mit ENOEXEC zurückkehrt execve(). Wenn Sie also versuchen, einen Befehl für die falsche Architektur auszuführen, ist es besser, eineführt keine Binärdatei ausDateifehler von Ihrer Shell als dass die Shell versucht, ihn als Shell-Skript zu verstehen.

Das ist bei POSIX erlaubt:

Wenn es sich bei der ausführbaren Datei nicht um eine Textdatei handelt, kann die Shell die Ausführung dieses Befehls umgehen.

Was in der nächsten Revision des Standardswird geändert in:

Die Shell kann eine heuristische Prüfung durchführen, um zu bestimmen, ob die auszuführende Datei ein Skript sein könnte, und kann die Ausführung dieses Befehls umgehen, wenn sie feststellt, dass die Datei kein Skript sein kann. In diesem Fall soll sie eine Fehlermeldung schreiben und einen Exit-Status von 126 zurückgeben.
Hinweis: Eine gängige Heuristik zum Ablehnen von Dateien, die kein Skript sein können, besteht darin, ein NUL-Byte vor einem <newline>-Byte innerhalb eines Präfixes mit fester Länge der Datei zu finden. Da sh Eingabedateien mit unbegrenzter Zeilenlänge akzeptieren muss, kann die heuristische Prüfung nicht auf der Zeilenlänge basieren.

Dieses Verhalten kann jedoch bei selbstextrahierenden Shell-Archiven im Weg stehen, die einen Shell-Header gefolgt von Binärdaten¹ enthalten.

Die zshShell unterstützt NUL in ihrer Eingabe. Beachten Sie jedoch, dass NULs nicht in den Argumenten von übergeben werden können execve(). Sie können sie daher nur in den Argumenten oder Namen von verwenden.eingebautBefehle oder Funktionen:

$ printf '\0() echo zero; \0\necho \0\n' | zsh | hd
00000000  7a 65 72 6f 0a 00 0a                              |zero...|
00000007

(hier wird eine Funktion mit NUL als Namen definiert und aufgerufen und ein NUL-Zeichen als Argument an den integrierten echoBefehl übergeben).

Manche entfernen sie, was auch sinnvoll ist. NULs werden manchmal als Füllzeichen verwendet. Sie werden beispielsweise von Terminals ignoriert (manchmal wurden sie an Terminals gesendet, um ihnen Zeit zu geben, komplexe Steuersequenzen (wie Wagenrücklauf (wörtlich)) zu verarbeiten). Löcher in Dateien werden so angezeigt, als wären sie mit NULs usw. gefüllt.

Beachten Sie, dass Nicht-Text nicht auf NUL-Bytes beschränkt ist. Es handelt sich auch um Bytefolgen, die in der Locale keine gültigen Zeichen bilden. Beispielsweise kann der Bytewert 0xc1 in UTF-8-codiertem Text nicht vorkommen. In Locales, die UTF-8 als Zeichencodierung verwenden, ist eine Datei, die ein solches Byte enthält, also keine gültige Textdatei und daher auch kein gültiges shSkript³.

In der Praxis yashist dies die einzige mir bekannte Shell, die sich über solche ungültigen Eingaben beschwert.


¹ In der nächsten Revision der Normes wird sich ändernZu

Die Eingabedatei kann von beliebigem Typ sein, aber der Anfangsteil der Datei, der gemäß der Shell-Grammatik (XREF zu XSH 2.10.2 Shell-Grammatikregeln) analysiert werden soll, muss aus Zeichen bestehen und darf kein NUL-Zeichen enthalten. Die Shell darf keine Zeilenlängenbeschränkungen erzwingen.

Shells müssen ausdrücklich Eingaben unterstützen, die mit einem syntaktisch gültigen Abschnitt ohne NUL-Bytes beginnen, auch wenn der Rest NULs enthält, um selbstextrahierende Archive zu berücksichtigen.

² und -Zeichen sollen gemäß der Zeichenkodierung des Gebietsschemas dekodiert werden (siehe Ausgabe von locale charmap), und auf POSIX-Systemen ist das NUL-Zeichen (dessen Kodierung immer Byte 0 ist) das einzige Zeichen, dessen Kodierung das Byte 0 enthält. Mit anderen Worten: UTF-16 gehört nicht zu den Zeichenkodierungen, die in einem POSIX-Gebietsschema verwendet werden können.

³ Es stellt sich jedoch die Frage der Gebietsschemaänderung innerhalb des Skripts (z. B. wenn die LANG/ LC_CTYPE/ LC_ALL/ LOCPATHVariablen zugewiesen werden) und an welchem ​​Punkt die Änderung für die Shell wirksam wird, die die Eingabe interpretiert.

Antwort2

Der Grund für dieses Verhalten ist etwas komplex …

Erstens enthalten moderne Shells eine Prüfung auf potenziell binäre Dateien (die Nullbytes enthalten), aber diese Prüfung überprüft nur die erste Zeile der Datei. Aus diesem Grund ändert das „#“ in der ersten Zeile das Verhalten. Die historische Bourne-Shell verfügt nicht über diese binäre Prüfung und benötigt das „#“ nicht einmal, um sich wie von Ihnen beschrieben zu verhalten.

Dann überspringt die spezielle Methode, die von der Bourne-Shell zur Unterstützung von Mehrbyte-Zeichen verwendet wird, mbtowc()einfach alle Null-Bytes, da mbtowc()für ein Null-Byte die Zeichenlänge 0 zurückgegeben wird, und dies führt zu einer Schleife, um das nächste Zeichen erneut zu versuchen.

Die Bourne Shell führte diese Art von Code etwa 1988 ein und es kann sein, dass andere Shells das Verhalten kopiert haben.

verwandte Informationen