Warum ist meine Umgebung voll von ␀s?

7

Ich habe einen Prozess, dessen Umgebung wie folgt ist:

root@a-vm:/proc/1363# hexdump -C environ
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0000016c

Ich habe so etwas noch nie gesehen. Ich erwarte environ, keine terminierten key=valuePaare zu enthalten , daher verletzt diese Ausgabe alle Arten von Behauptungen. Betrachte ich einen bekannten Kernel-Fehler oder gibt es unter Unix / Linux einen legitimen Weg, dies zu erreichen? (… Und wenn ja, warum? Warum lässt der Kernel diesen Unsinn überhaupt zu?)

(unter Linux, 3.13.0 / Ubuntu Trusty)

(Ich lief dies in bei dem Versuch , herauszufinden, warum dieser Prozess ist nicht eine vorübergehende Ausgabe an die richtige Stelle zu schreiben, es ist Angenommen , ein bestimmtes Verzeichnis für temporäre Speicherung zu verwenden, und es informiert dieses Verzeichnisses die env Variable über die Einstellung TMP, aber ich‘ m Einstellung TMPzu etwas , das aussieht wie ein ganz normaler Weg, nicht ein Haufen von nuls, und ich habe nie sowieso ein völlig leer env gesehen.)

Thanatos
quelle
/ proc / <pid> / environ ist die Anfangsumgebung für den Prozess. Aus der Ausgabe geht hervor, dass der Prozess mit PID 1363 mit einer leeren Anfangsumgebung gestartet wurde.
Andy Dalton
2
Wenn das wahr wäre, wäre die Datei dann nicht entweder leer oder ein einzelnes ␀, nicht Hunderte von ihnen?
Thanatos
Hängt davon ab, ob Sie einen NULL-Zeiger übergeben oder ob Sie einen Puffer übergeben, der Nullzeichen enthält. Wenn Sie NULL übergeben, wird eine leere Datei angezeigt.
Andy Dalton

Antworten:

16

Dies ist kein Unsinn, es gibt unter Linux einen legitimen Weg, dies zu erreichen, und Ihre Erwartungen sind falsch.

Die Argument- und Umgebungszeichenfolgen, die vom Kernel an den Startcode eines Programms übergeben werden, werden wie alle anderen Programmdaten im normalen virtuellen Speicher des Anwendungsbereichs gespeichert. und wie alle anderen Programmdatenvariablen können sie geändert werden. Es ist durchaus legitim, dass Programme sie ändern.

(Beachten Sie, dass dies unter dem Gesichtspunkt erfolgt, was der Kernel bereitstellt und erzwingt. Was die Standards für bestimmte Programmiersprachen möglicherweise aussagen, ist nicht unbedingt dasselbe. Was den Kernel betrifft, ist dies nur ein Bereich des Anwendungsbereichs virtueller Speicher für Programmdaten, die lesbar und beschreibbar sind. Dem Kernel ist es egal, aus welcher Programmiersprache Sie Ihren Maschinencode kompiliert haben.)

Die /proc/${PID}/environDatei ist nur ein Fenster zu diesem virtuellen Speicher im Anwendungsbereich. Anstatt sich an die tatsächlichen Umgebungsdaten des Prozesses zu erinnern, merkt sich Linux nur die Start- und Endadressen des Umgebungsbereichs, mit dem der Prozess gestartet wurde, und die /proc/${PID}/environDatei liest nur das aus, was sich gerade in diesem Speicher befindet. Sie sollten nicht erwarten, dass diese Datei eine Liste von ␀-terminierten Zeichenfolgen enthält. Das ist eine falsche Erwartung.

Es gibt keine GNU C-Bibliotheksfunktion zum Ändern des Speichers, der diese Zeichenfolgen enthält. Dafür haben verschiedene Programme ihre eigenen Funktionen.

Betrachten Sie als Beispiel OpenSSH. Der OpenSSH-Server ändert, was psfür seinen Argumentvektor angezeigt wird, um Dinge wie zu lesen sshd: JdeBP [priv].

Der OpenSSH-Server enthält Code, der versucht, unter Linux nachzuahmen, was er mit der BSD C-Bibliothek unter OpenBSD tun kann. Unter OpenBSD gibt es eine BSD C-Bibliotheksfunktion mit dem Namen setproctitle(), die den vom psBefehl angegebenen Prozessargumentvektor neu schreibt . Es wird aufgerufen sysctl(), einen neuen Argumentvektor an den Kernel zu übergeben, der psmit auslesen kann sysctl(). FreeBSD hat eine ähnliche Funktion.

Unter Linux erinnert sich der Kernel, wie erläutert, nicht an tatsächliche Argumente und Umgebungen, sondern lediglich an die Start- und Endadressen der Speicherbereiche, in denen er sie beim Starten des Prozesses ursprünglich platziert hat. Der Linux-Port von OpenSSH verfügt also über eine Kompatibilitätsfunktion setproctitle(), die stattdessen den oben genannten Speicherbereich überschreibt.

Diese Kompatibilitätsfunktion berechnet die Gesamtgröße des Umgebungsbereichs und des Argumentbereichs und überschreibt alles mit der neuen Argumentzeichenfolge. Dies geschieht, weil Programme, die aufrufen setproctitle(), im Normalfall einen längeren Satz von Argumentdaten schreiben möchten als ursprünglich. sshdoft tut. Auf diese Weise können die neuen Argumente den Umgebungsbereich überschreiben, der dem Argumentbereich folgt, und den Programmen mehr Platz für längere Sätze von Argumentzeichenfolgen geben.

Wichtig ist auch , dass der nicht verwendete Teil des Bereichs , den es nicht zum Überschreiben benötigt hat, auf die ursprüngliche Länge der gesamten Argument- und Umgebungsdaten mit ␀s aufgefüllt wird.

Und was Sie sehen, ist das genaue Ergebnis davon. Wenn Sie einen OpenSSH-Serverprozess auf Ihrem System finden, werden Sie feststellen, dass auch dieser viele ␀s enthält /proc/${PID}/environ.

Weiterführende Literatur

JdeBP
quelle
1
Es ist zu beachten, dass die setproctitleImplementierungen, die einige Projekte unter Linux durchführen (z. B. über libbsd), alle von schwerwiegendem undefiniertem Verhalten geprägt sind und nicht verwendet werden sollten. Der Speicher, der Ihnen nicht gehört (z. B. auf den die Standardbibliothek, der dynamische Linker usw. möglicherweise verweisen), um hübsche Nachrichten in der ps / top-Ausgabe anzuzeigen, ist absolut nicht zu rechtfertigen.
R .. GitHub STOP HELPING ICE
2
@R .. Ich sehe nicht, wie das Überschreiben dieses Speichers C undefiniertes Verhalten wäre, selbst wenn andere Teile des Prozesses Verweise darauf haben. Könnten Sie näher darauf eingehen?
Marcelm
1
@ Marcelm: OK. Zu Beginn müssen wir POSIX annehmen, da es sonst keinen environZeiger gibt, mit dem man überhaupt in den Speicher gelangen kann. In environPOSIX heißt es: "Jede Anwendung, die die Zeiger, auf die die Umgebungsvariable zeigt, direkt ändert, weist ein undefiniertes Verhalten auf." Es scheint zulässig zu sein, die Zeichenfolgen, auf die verwiesen wird, zu ändern (vorbehaltlich Einschränkungen bei der Speichersynchronisierung), aber es ist nicht klar, ob definiert ist, sie in einem Formular zu belassen, in dem sie keine gültigen Umgebungseinträge ( "X=Y"Formular) sind.
R .. GitHub STOP HELPING ICE
1
Eine Sache, die mit Sicherheit nicht gültig (gut definiert) ist, ist die Zeigerarithmetik über das Ende des Strings hinaus environ[0]und / oder sich darauf zu verlassen, dass sie bis zum Ende stößt (und als kombiniertes Objekt betrachtet werden kann) environ[1]. Insbesondere wenn die environ[0]Änderung nach einer Änderung nicht auf Null gesetzt wird (wie dies setproctitledazu führen kann, dass der Nachrichtentext darüber gelegt wird), erfolgt die Zeigerarithmetik außerhalb der Grenzen später, wenn auf die Zeichenfolge zugegriffen wird.
R .. GitHub STOP HELPING ICE
2

Dies ist völlig machbar, indem Sie NULs in den Speicherort schreiben, in dem sich die Umgebungsvariablen befinden:

#include <stdio.h>
#include <unistd.h>

extern char **environ;

int main(void)
{
    int i;
    char *p = *environ;
    /* hopefully your ENV is longer than this */
    for (i = 0; i < 10; i++) *(p + i) = 0;
    printf("hexdump -C /proc/%d/environ\n", getpid());
    sleep(99999);
}

Wenn Sie stattdessen ein Programm mit einer leeren Umgebung starten, ist die environDatei vollständig leer:

execle("/bin/sleep", "sleep", "999", (char *)NULL, (char *const) NULL)

Dieser Fall wird also von oder mit dem Prozess ausgeführt, sobald er ausgeführt wird (und es gibt wenig, was dies verhindern kann, es sei denn, Sie haben diesen Speicher irgendwie gesperrt und setenv(3)Aufrufe könnten problematisch sein ...).

Thrig
quelle
Ich denke, Sie werden feststellen, dass dies falsch ist. /proc/$pid/environspürt das environObjekt nicht auf (und hat keine Möglichkeit dazu, da die ausführbare Datei möglicherweise nicht einmal Symbole enthält) und folgt Zeigern. Es gibt einfach den Inhalt des Speicherbereichs aus, den der Kernel zum Übergeben der anfänglichen Umgebungswerte verwendet hat.
R .. GitHub STOP HELPING ICE
@R .. wenn es falsch ist, warum hat dann hexdumpauf Centos 6 genau 10 angezeigt, NULdie über den Start der Umgebung für den Prozess geschrieben wurden?
Thrig
Oh, sorry, ich habe es falsch verstanden. Es überschreibt tatsächlich die Zeichenfolgen, auf die von gezeigt wird environ[0], nicht die Zeiger an environ[]sich.
R .. GitHub STOP HELPING ICE