Erlaubt ISO C Aliasing der argv[] Zeiger an main() übergeben?

Lesezeit: 11 Minuten

Benutzer-Avatar
Ich werde nicht existieren Ich werde nicht existieren

ISO C erfordert, dass gehostete Implementierungen eine Funktion namens aufrufen main. Wenn das Programm Argumente empfängt, werden sie als Array von empfangen char* Zeiger, das zweite Argument in mains Definition int main(int argc, char* argv[]).

ISO C erfordert auch, dass die Zeichenfolgen, auf die die argv Array änderbar sein.

Aber können die Elemente von argv alias einander? Mit anderen Worten, kann es existieren i, j so dass

  • 0 >= i && i < argc
  • 0 >= j && j < argc
  • i != j
  • 0 < strlen(argv[i])
  • strlen(argv[i]) <= strlen(argv[j])
  • argv[i] Pseudonyme argv[j]

beim Programmstart? Wenn ja, durchschreiben argv[i][0] würde auch durch die Aliasing-Zeichenfolge gesehen werden argv[j].

Die relevanten Klauseln des ISO C-Standards sind unten aufgeführt, erlauben mir jedoch nicht, die Titelfrage abschließend zu beantworten.

§ 5.1.2.2.1 Programmstart

Die beim Programmstart aufgerufene Funktion wird benannt main. Die Implementierung deklariert keinen Prototyp für diese Funktion. Es muss mit einem Rückgabetyp von definiert werden int und ohne Parameter:

int main(void) { /* ... */ }

oder mit zwei Parametern (hier als argc und argvobwohl beliebige Namen verwendet werden können, da sie lokal für die Funktion sind, in der sie deklariert sind):

int main(int argc, char *argv[]) { /* ... */ }

oder gleichwertig; 10) oder auf eine andere implementierungsdefinierte Weise.

Wenn sie deklariert sind, werden die Parameter an die main Die Funktion unterliegt den folgenden Einschränkungen:

  • Der Wert von argc soll nichtnegativ sein.
  • argv[argc] soll ein Nullzeiger sein.
  • Wenn der Wert von argc größer als Null ist, die Array-Mitglieder argv[0] durch argv[argc-1] inclusive soll Zeiger auf Strings enthalten, denen vor dem Programmstart implementierungsdefinierte Werte von der Host-Umgebung gegeben werden. Die Absicht besteht darin, dem Programm Informationen bereitzustellen, die vor dem Programmstart von einer anderen Stelle in der gehosteten Umgebung bestimmt wurden. Wenn die Hostumgebung keine Zeichenfolgen mit Groß- und Kleinbuchstaben liefern kann, muss die Implementierung sicherstellen, dass die Zeichenfolgen in Kleinbuchstaben empfangen werden.
  • Wenn der Wert von argc größer als Null ist, die Zeichenfolge, auf die von gezeigt wird argv[0] stellt den Programmnamen dar; argv[0][0] soll das Nullzeichen sein, wenn der Programmname nicht aus der Hostumgebung verfügbar ist. Wenn der Wert von argc größer als eins ist, wird auf die Zeichenfolgen verwiesen argv[1] durch argv[argc-1] stellen die Programmparameter dar.
  • Die Parameter argc und argv und die Zeichenfolgen, auf die durch die hingewiesen wird argv Array soll durch das Programm änderbar sein und ihre zuletzt gespeicherten Werte zwischen Programmstart und Programmende beibehalten.

Nach meiner Lektüre lautet die Antwort auf die Titelfrage “Ja”, da dies nirgendwo ausdrücklich verboten ist und nirgendwo der Standard die Verwendung von fordert oder erfordert char* restrict*-qualifiziert argvaber die Antwort könnte die Interpretation von betreffen “und behalten ihre zuletzt gespeicherten Werte zwischen Programmstart und Programmende bei.”.

Die praktische Bedeutung dieser Frage ist, dass, wenn die Antwort darauf tatsächlich “Ja” lautet, ein portables Programm die Zeichenfolgen ändern möchte argv muss zuerst (das Äquivalent von) POSIX ausführen strdup() auf sie zur Sicherheit.

  • Wie Sie erwähnen strdupich würde empfehlen, auch POSIX zu überprüfen, da dies Ihre Umgebung zu sein scheint. strdup ist nicht Bestandteil des C-Standards.

    – zu ehrlich für diese Seite

    10. Juni 2018 um 1:10 Uhr

  • Wenn so etwas für mein Programm superwichtig wäre, würde ich einen Test schreiben, um das zu überprüfen. Verwenden Sie die POSIX execl um ein Array von Argumentzeigern zu übergeben, die einen Aliasnamen haben, und dann sehen, was im Programm passiert.

    – Zan Luchs

    10. Juni 2018 um 1:17 Uhr

  • @ZanLynx: Nehmen wir an, der Test zeigt, dass separate Zeichenfolgen verwendet werden, dh kein Aliasing. Was beweist das?

    – zu ehrlich für diese Seite

    10. Juni 2018 um 1:37 Uhr

  • @Peter: Obwohl ich die gleiche Position habe, ist es in der Tat nur ein Indikator, keine Garantie. Modifizierbar bedeutet kein Non-Aliasing. Ich wäre ziemlich überrascht, wenn es Aliasing geben würde.

    – zu ehrlich für diese Seite

    10. Juni 2018 um 1:45 Uhr

  • @Peter Als Embedded-Entwickler bin ich mir all dessen sehr wohl bewusst. Einschließlich der nicht programmierrelevanten Punkte. Trotzdem ist es eine erfrischend interessante Frage im Vergleich zu dem, was in den letzten Jahren normalerweise neu im C-Tag war. Ich überlasse es Ihnen, Ihre Schlussfolgerungen zu ziehen, was uns dies über die Qualität der durchschnittlichen C-Frage aussagt :-\

    – zu ehrlich für diese Seite

    10. Juni 2018 um 23:28 Uhr


Benutzer-Avatar
John Bollinger

Nach meiner Lektüre lautet die Antwort auf den Titular “Ja”, da es nirgendwo ausdrücklich verboten ist und nirgendwo der Standard die Verwendung von eingeschränkt qualifiziertem argv fordert oder verlangt, aber die Antwort könnte sich auf die Interpretation von “und behält ihren letzten -gespeicherte Werte zwischen Programmstart und Programmende.”.

Ich stimme zu, dass der Standard Elementen des Argumentvektors nicht ausdrücklich verbietet, Aliase voneinander zu sein. Ich glaube nicht, dass die Modifizierbarkeits- und Werterhaltungsbestimmungen dieser Position widersprechen, aber sie deuten für mich darauf hin, dass das Komitee die Möglichkeit des Aliasing nicht in Betracht gezogen hat.

Die praktische Bedeutung dieser Frage ist, dass, wenn die Antwort darauf tatsächlich “Ja” lautet, ein portables Programm, das die Zeichenfolgen in argv ändern möchte, aus Sicherheitsgründen zuerst (das Äquivalent von) POSIX strdup() auf ihnen ausführen muss.

Genau aus diesem Grund denke ich, dass das Komitee diese Möglichkeit nicht einmal in Erwägung gezogen hat. Wenn sie das getan hätten, hätten sie sicherlich zumindest eine Fußnote mit demselben Effekt eingefügt oder ausdrücklich angegeben, dass die Argumentzeichenfolgen alle unterschiedlich sind.

Ich neige dazu zu glauben, dass dieses Detail der Aufmerksamkeit des Komitees entgangen ist, da Implementierungen in der Praxis tatsächlich unterschiedliche Zeichenfolgen bereitstellen und es außerdem selten vorkommt, dass Programme ihre Argumentzeichenfolgen ändern (obwohl sie modifizieren argv selbst ist etwas häufiger). Wenn das Komitee einer offiziellen Interpretation in diesem Bereich zustimmen würde, wäre ich nicht überrascht, wenn sie sich gegen die Möglichkeit des Aliasings wehren würden.

Bis und sofern eine solche Interpretation nicht herausgegeben wird, haben Sie jedoch Recht, dass Sie sich bei strikter Konformität nicht darauf verlassen können a priori an argv Elemente werden nicht gealiased.

  • Ich denke, die Aussage, dass geänderte Zeichenfolgen ihren geänderten Wert behalten, reicht aus, um sicherzustellen, dass die Zeichenfolgen weder für andere Argumente noch für Umgebungsvariablen noch für einen versteckten veränderlichen Zustand Alias ​​sind.

    – Rici

    10. Juni 2018 um 3:26 Uhr

  • @rici, während ich zustimme, wie in dieser Antwort dargestellt, dass das Komitee nicht beabsichtigte, zuzulassen, dass die Argumentzeichenfolgen miteinander (oder mit Umgebungsvariablen) verknüpft werden, ist die Anforderung, dass sie “ihren zuletzt gespeicherten Wert beibehalten”, dies der Fall nicht dazu sprechen. Es sei denn, Sie beginnen natürlich damit, die Schlussfolgerung anzunehmen. Aliasing würde nur zusätzliche Möglichkeiten bieten, einen Wert zu speichern.

    – Johannes Bollinger

    10. Juni 2018 um 11:55 Uhr


  • @JohnBollinger: Zu sagen, dass ein Objekt “seinen zuletzt gespeicherten Wert beibehält”, impliziert, dass sich sein Wert nicht als Reaktion auf Aktionen ändert, die dieses Objekt weder direkt noch über einen davon abgeleiteten Zeiger betreffen. Wenn ein Objekt mit einem Alias ​​versehen wird, dessen Wert sich auf andere Weise ändert, würde dieses Objekt aufhören, seinen zuletzt gespeicherten Wert beizubehalten.

    – Superkatze

    12. Juni 2018 um 18:36 Uhr

  • Aber wenn zwei argv Elemente gleich sind, @supercat, dann ist die Verwendung eines von ihnen für den Zugriff auf die Argumentzeichenfolge, auf die gezeigt wird, tatsächlich eine Aktion, die einen Zeiger beinhaltet, der von diesem Objekt (von der Umgebung) abgeleitet wird. Damit die Werterhaltungsvorschrift Aliasing aus diesen Gründen verbietet, ist eine Schwellenwertannahme von Non-Aliasing erforderlich, so dass auf diese Weise nichts bewiesen ist.

    – Johannes Bollinger

    12. Juni 2018 um 20:11 Uhr

  • Das sehe ich überhaupt nicht, @supercat. Ich kann die Aussage genau zum Nennwert interpretieren und tue es auch, indem ich den Unterschied zwischen Zeigerwerten und den Objekten, auf die sie zeigen, erkenne. Das sagt mir, dass sich die Argumentzeichenfolgen nur als Ergebnis von Programmaktionen und gemäß der C-Semantik ändern werden, nicht willkürlich, und es gibt mir daher ein ganzes Universum von Programmaktionen, von denen ich sicher sein kann, dass sie sie nicht ändern werden. Dass es unbestimmt lässt, ob bestimmte andere Handlungen sie ändern werden, macht die Bestimmung nicht bedeutungslos.

    – Johannes Bollinger

    13. Juni 2018 um 0:36 Uhr

So funktioniert es auf gängigen *nix-Plattformen (einschließlich Linux und Mac OS, vermutlich auch FreeBSD). argv ist ein Array von Zeigern in einen einzelnen Speicherbereich, der die Argumentzeichenfolgen nacheinander enthält (nur durch das Null-Terminator getrennt). Verwenden execl() ändert dies nicht – selbst wenn der Aufrufer denselben Zeiger mehrmals übergibt, wird die Quellzeichenfolge mehrmals kopiert, ohne besonderes Verhalten für identische (dh Alias-)Zeiger (ein ungewöhnlicher Fall ohne großen Optimierungsvorteil).

C benötigt diese Implementierung jedoch nicht. Der wirklich Paranoiker möchte vielleicht jede Zeichenfolge kopieren, bevor er sie ändert, und vielleicht die Kopien überspringen, wenn der Speicher begrenzt ist und eine Schleife übergeht argv zeigt, dass keiner der Zeiger tatsächlich ein Alias ​​ist (zumindest unter denen, die das Programm zu modifizieren beabsichtigt). Dies erscheint übermäßig paranoid, es sei denn, Sie entwickeln Flugsoftware oder ähnliches.

  • “Flugsoftware und dergleichen” würde zunächst keine Befehlszeilenargumente modifizieren – der Aufwand / die Kosten für die Bereitstellung von Sicherheitsnachweisen würden die Vorteile, die es Entwicklern ermöglicht, solche Abkürzungen zu verwenden, um Größenordnungen übersteigen. Selbst wenn (hypothetisch) solche Dinge erlaubt wären, ist die Sicherheit der Toolchain (und des Codes, den sie ausgibt oder verwendet) genauso wichtig wie die Sicherheit der tatsächlich erstellten Software – es würde eine Gegenprüfung der beim Systemdesign getroffenen Annahmen gegen das Verhalten von geben die Werkzeugkette. Der Compiler und sein Startcode werden nicht als Blackbox behandelt.

    – Petrus

    10. Juni 2018 um 2:41 Uhr

  • @Peter: Ich bin mir nicht sicher, welchen Teil des Szenarios Sie als “Abkürzung nehmen” betrachten.

    – Johannes Zwinck

    10. Juni 2018 um 2:47 Uhr

  • Ich vermute, dass die „Verknüpfung“ der Teil ist, in dem die Umgebung, in der ein Programm ausgeführt wird, die „Speichernutzung reduzieren“ darf, indem gemeinsame Zeichenfolgen in der Argumentliste denselben Speicher verwenden, sodass die Argumentzeichenfolgen nicht unabhängig voneinander sind .

    – Jonathan Leffler

    10. Juni 2018 um 3:52 Uhr

  • Geschäftskritische Software verwendet niemals Befehlszeilen-Junk oder stdio.h.

    – Ludin

    11. Juni 2018 um 9:46 Uhr

  • @Lundin: Ich bin nicht davon überzeugt, dass geschäftskritische Programme nicht verwendet würden argv. Und ich habe nie etwas im Zusammenhang mit erwähnt stdio.h.

    – Johannes Zwinck

    11. Juni 2018 um 11:50 Uhr

Benutzer-Avatar
Steve Gipfel

Als Datenpunkt habe ich die folgenden Programme kompiliert und auf mehreren Systemen ausgeführt. (Haftungsausschluss: Diese Programme sollen einen Datenpunkt bereitstellen, aber wie wir sehen werden, tun sie dies nicht Beantworten Sie am Ende die Frage wie angegeben.)

p1.c:

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

int main()
{
    char test[] = "test";
    execl("./p2", "p2", test, test, NULL);
}

p2.c:

#include <stdio.h>

int main(int argc, char **argv)
{
    int i;
    for(i = 1; i < argc; i++) printf("%s ", argv[i]); printf("\n");
    argv[1][0] = 'b';
    for(i = 1; i < argc; i++) printf("%s ", argv[i]); printf("\n");
}

An jedem Ort, an dem ich es ausprobiert habe (unter MacOS und verschiedenen Unix- und Linux-Varianten), wurde es gedruckt

test test 
best test 

Da die zweite Zeile nie “best best” beweist dies, dass auf den getesteten Systemen zum Zeitpunkt der Ausführung des zweiten Programms die Zeichenfolgen nicht mehr mit Alias ​​versehen sind.

Natürlich tut dieser Test nicht beweise, dass Saiten drin sind argv kann unter keinen Umständen unter irgendeinem System da draußen als Alias ​​verwendet werden. Ich denke, alles, was es beweist, ist, dass, wenig überraschend, jedes der getesteten Betriebssysteme die Argumentliste zwischendurch mindestens einmal neu kopiert p1 Anrufe execl und die zeit das p2 tatsächlich aufgerufen wird. Mit anderen Worten, der vom aufrufenden Programm konstruierte Argumentvektor ist nicht direkt im aufgerufenen Programm verwendet und beim Kopieren (wieder nicht überraschend) “normalisiert”, was bedeutet, dass die Effekte eines Aliasings verloren gehen.

(Ich sage, das ist nicht überraschend, denn wenn Sie darüber nachdenken, wie die exec Familie von Systemaufrufen tatsächlich funktionieren, und die Art und Weise, wie der Prozessspeicher unter Unix-ähnlichen Systemen ausgelegt ist, gibt es keine Möglichkeit, dass die Argumentliste des aufrufenden Programms direkt verwendet werden könnte; es hat mindestens einmal in den Adressraum des neuen, ausgeführten Prozesses kopiert werden. Darüber hinaus wird jede offensichtliche und einfache Methode zum Kopieren der Argumentliste sie immer und automatisch auf diese Weise “normalisieren”. der Kernel müsste erhebliche, zusätzliche, völlig unnötige Arbeit leisten, um Aliasing zu erkennen und zu bewahren.)

Nur für den Fall, dass es darauf ankommt, habe ich das erste Programm folgendermaßen modifiziert:

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

int main()
{
    char test[] = "test";
    char *argv[] = {"p2", test, test, NULL};
    execv("./p2", argv);
}

Die Ergebnisse waren unverändert.


Nach alledem stimme ich zu, dass dieses Problem wie ein Versehen oder ein Buglet in den Standards erscheint. Mir ist keine Klausel bekannt, die garantiert, dass die Zeichenfolgen auf by verweisen argv sind unterschiedlich, was bedeutet, dass ein paranoid geschriebenes Programm sich wahrscheinlich nicht auf eine solche Garantie verlassen kann, egal wie wahrscheinlich es ist, dass (wie diese Antwort zeigt) jede vernünftige Implementierung dies wahrscheinlich so macht.

1106530cookie-checkErlaubt ISO C Aliasing der argv[] Zeiger an main() übergeben?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy