Warum ist printf mit einem einzigen Argument (ohne Konvertierungsbezeichner) veraltet?

Lesezeit: 11 Minuten

Benutzeravatar von StackUser
Stapelbenutzer

In einem Buch, das ich gerade lese, steht das geschrieben printf mit einem einzelnen Argument (ohne Konvertierungsbezeichner) ist veraltet. Es empfiehlt sich zu ersetzen

printf("Hello World!");

mit

puts("Hello World!");

oder

printf("%s", "Hello World!");

Kann mir jemand sagen warum printf("Hello World!"); ist falsch? Im Buch steht geschrieben, dass es Schwachstellen enthält. Was sind diese Schwachstellen?

  • Notiz: printf("Hello World!") ist nicht das Gleiche wie puts("Hello World!"). puts() hängt a an '\n'. Vergleichen Sie stattdessen printf("abc") zu fputs("abc", stdout)

    – chux – Wiedereinsetzung von Monica

    8. Juli 2015 um 12:36 Uhr


  • Was ist das für ein Buch? Ich denke nicht printf ist auf die gleiche Weise veraltet wie zum Beispiel gets ist in C99 veraltet, daher können Sie Ihre Frage bearbeiten, um sie präziser zu machen.

    – el.pescado – нет войне

    9. Juli 2015 um 18:53 Uhr

  • Es hört sich so an, als ob das Buch, das Sie gerade lesen, nicht sehr gut ist – ein gutes Buch sollte nicht nur sagen, dass so etwas “veraltet” ist (das ist faktisch falsch, es sei denn, der Autor verwendet das Wort, um seine eigene Meinung zu beschreiben) und sollte erklären, welche Verwendung tatsächlich ungültig und gefährlich ist, anstatt sicheren/gültigen Code als Beispiel für etwas anzuzeigen, das Sie “nicht tun sollten”.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    9. Juli 2015 um 23:14 Uhr

  • Kannst du das Buch identifizieren?

    – Keith Thompson

    11. Juli 2015 um 9:34 Uhr

  • Bitte geben Sie Buchtitel, Autor und Seitenangabe an. Danke.

    – Greenonline

    11. Juli 2015 um 11:57 Uhr

Benutzeravatar von Jabberwocky
Jabberwocky

printf("Hello World!"); ist IMHO nicht anfällig, aber bedenken Sie Folgendes:

const char *str;
...
printf(str);

Wenn str zeigt zufällig auf eine Zeichenfolge, die enthält %s Formatbezeichner zeigt Ihr Programm ein undefiniertes Verhalten (meistens einen Absturz), während puts(str) zeigt die Zeichenfolge einfach so an, wie sie ist.

Beispiel:

printf("%s");   //undefined behaviour (mostly crash)
puts("%s");     // displays "%s\n"

  • Abgesehen davon, dass das Programm zum Absturz gebracht wird, gibt es viele andere Exploits, die mit Formatzeichenfolgen möglich sind. Weitere Informationen finden Sie hier: en.wikipedia.org/wiki/Unkontrollierte_format_zeichenfolge

    – e.dan

    8. Juli 2015 um 11:19 Uhr

  • Ein weiterer Grund ist das puts geht vermutlich schneller.

    – edmz

    8. Juli 2015 um 11:44 Uhr

  • @Schwarz: puts ist “vermutlich” schneller, und das ist wahrscheinlich ein weiterer Grund, warum die Leute es empfehlen, aber das ist es nicht eigentlich Schneller. Ich habe gerade gedruckt "Hello, world!" 1.000.000 Mal, in beide Richtungen. Mit printf es dauerte 0,92 Sekunden. Mit puts es dauerte 0,93 Sekunden. Es gibt Dinge, über die man sich Sorgen machen muss, wenn es um Effizienz geht, aber printf vs. puts ist keiner von ihnen.

    – Steve Summit

    8. Juli 2015 um 14:21 Uhr


  • @KonstantinWeitz: Aber (a) ich habe gcc nicht verwendet und (b) es spielt keine Rolle warum der Anspruch “puts ist schneller” falsch ist, ist es immer noch falsch.

    – Steve Gipfel

    8. Juli 2015 um 16:15 Uhr


  • gcc automatisch konvertiert printf zu puts Wenn es nur ein einziges Argument gibt, enthält der Formatstring kein %-Feld und wird mit beendet '\n'. Dafür müssen keine Optimierungen aktiviert werden. Schauen Sie sich einfach den Assembler-Code an, der von erstellt wurde gcc -S.

    – dlask

    14. Juli 2015 um 19:09 Uhr

printf("Hello world");

ist in Ordnung und hat keine Sicherheitslücke.

Das Problem liegt bei:

printf(p);

wo p ist ein Zeiger auf eine Eingabe, die vom Benutzer gesteuert wird. Es ist anfällig für Format-Strings-Angriffe: Benutzer kann Konvertierungsspezifikationen einfügen, um die Kontrolle über das Programm zu übernehmen, z. %x Speicher ausleeren bzw %n Speicher zu überschreiben.

Beachten Sie, dass puts("Hello world") ist im Verhalten nicht gleichbedeutend mit printf("Hello world") aber zu printf("Hello world\n"). Compiler sind normalerweise schlau genug, um den letzteren Aufruf zu optimieren, um ihn durch zu ersetzen puts.

  • Na sicher printf(p,x) wäre genauso problematisch, wenn der Benutzer die Kontrolle darüber hat p. Das Problem ist also nicht die Verwendung von printf mit nur einem Argument, sondern mit einem benutzergesteuerten Formatstring.

    – Hagen von Eitzen

    9. Juli 2015 um 5:10 Uhr

  • @HagenvonEitzen Das ist technisch richtig, aber nur wenige würden absichtlich eine vom Benutzer bereitgestellte Formatzeichenfolge verwenden. Wenn Menschen schreiben printf(p)weil sie nicht erkennen, dass es sich um eine Formatzeichenfolge handelt, sie denken nur, dass sie ein Literal drucken.

    – Barmar

    15. Juli 2015 um 13:54 Uhr

Weiter zu den anderen Antworten, printf("Hello world! I am 50% happy today") ist ein leicht zu machender Fehler, der möglicherweise alle möglichen fiesen Speicherprobleme verursacht (es ist UB!).

Es ist einfach einfacher, einfacher und robuster, von Programmierern absolute Klarheit zu „verlangen“. wenn sie eine wörtliche Zeichenfolge und sonst nichts wollen.

Und das ist was printf("%s", "Hello world! I am 50% happy today") bekommt dich. Es ist absolut idiotensicher.

(Steve natürlich printf("He has %d cherries\n", ncherries) ist absolut nicht dasselbe; In diesem Fall ist der Programmierer nicht in der Denkweise “wörtliche Zeichenfolgen”. sie ist in der “Formatzeichenfolge”-Denkweise.)

  • Dies ist kein Argument wert, und ich verstehe, was Sie über die Denkweise wörtlich vs. Format-String sagen, aber, nun ja, nicht jeder denkt so, was einer der Gründe dafür ist, dass Einheitsregeln ärgern können. Sagen Sie “drucken Sie niemals konstante Zeichenfolgen mit printf“ ist ungefähr so, als würde man sagen: „Immer schreiben if(NULL == p). Diese Regeln mögen für manche Programmierer nützlich sein, aber nicht für alle. Und in beiden Fällen (mismatched printf Formate und Yoda-Bedingungen) warnen moderne Compiler ohnehin vor Fehlern, sodass die künstlichen Regeln noch weniger wichtig sind.

    – Steve Gipfel

    8. Juli 2015 um 13:55 Uhr


  • @Steve Wenn es genau null Vorteile gibt, etwas zu verwenden, aber einige Nachteile, dann gibt es wirklich keinen Grund, es zu verwenden. Yoda-Bedingungen andererseits tun haben den Nachteil, dass sie den Code schwerer lesbar machen (man würde intuitiv sagen „wenn p Null ist“ und nicht „wenn Null p ist“).

    – Voo

    9. Juli 2015 um 13:31 Uhr

  • @Voo printf("%s", "hello") wird langsamer sein als printf("hello"), also gibt es einen Nachteil. Ein kleiner, weil IO fast immer viel langsamer ist als eine solche einfache Formatierung, aber ein Nachteil.

    – Yakk – Adam Nevraumont

    9. Juli 2015 um 14:41 Uhr

  • @ Yakk Ich bezweifle, dass das langsamer wäre

    – MM

    22. September 2017 um 3:44 Uhr

  • gcc -Wall -W -Werror verhindert schlimme Folgen aus solchen Fehlern.

    – chqrlie

    14. Februar 2020 um 20:14 Uhr

Benutzeravatar von P1kachu
P1kachu

Ich füge nur ein paar Informationen hinzu bezüglich der Schwachstelle Teil hier.

Es soll wegen der Schwachstelle im printf-String-Format anfällig sein. In Ihrem Beispiel, in dem die Zeichenfolge fest codiert ist, ist sie harmlos (auch wenn das Festcodieren solcher Zeichenfolgen niemals vollständig empfohlen wird). Es ist jedoch eine gute Angewohnheit, die Typen der Parameter anzugeben. Nehmen Sie dieses Beispiel:

Wenn jemand anstelle eines regulären Strings ein Format-String-Zeichen in Ihr printf einfügt (z. B. wenn Sie das Programm stdin drucken möchten), nimmt printf alles, was er kann, auf den Stapel.

Es wurde (und wird immer noch) häufig verwendet, um Programme auszunutzen, um Stapel zu durchsuchen, um beispielsweise auf versteckte Informationen zuzugreifen oder die Authentifizierung zu umgehen.

Beispiel (C):

int main(int argc, char *argv[])
{
    printf(argv[argc - 1]); // takes the first argument if it exists
}

wenn ich als Eingang dieses Programms setze "%08x %08x %08x %08x %08x\n"

printf ("%08x %08x %08x %08x %08x\n"); 

Dies weist die printf-Funktion an, fünf Parameter aus dem Stack abzurufen und sie als 8-stellige aufgefüllte Hexadezimalzahlen anzuzeigen. Eine mögliche Ausgabe könnte also so aussehen:

40012980 080628c4 bffff7a4 00000005 08059c04

Sehen Dies für eine vollständigere Erklärung und andere Beispiele.

Benutzeravatar von Steve Summit
Steve Gipfel

Das ist ein falscher Rat. Ja, wenn Sie eine Laufzeitzeichenfolge zum Drucken haben,

printf(str);

ist ziemlich gefährlich, und Sie sollten immer verwenden

printf("%s", str);

sondern weil man im Allgemeinen nie wissen kann, ob str könnte ein enthalten % Schild. Wenn Sie jedoch eine Kompilierzeit haben Konstante String, daran ist überhaupt nichts auszusetzen

printf("Hello, world!\n");

(Unter anderem ist das das klassischste C-Programm aller Zeiten, buchstäblich aus dem C-Programmierbuch von Genesis. Also ist jeder, der diese Verwendung ablehnt, ziemlich ketzerisch, und ich für meinen Teil wäre etwas beleidigt!)

  • because printf's first argument is always a constant string Ich bin mir nicht ganz sicher, was du damit meinst.

    – Sebastian Mach

    8. Juli 2015 um 13:57 Uhr

  • Wie ich sagte, "He has %d cherries\n" ist eine konstante Zeichenfolge, was bedeutet, dass es sich um eine Konstante zur Kompilierzeit handelt. Aber um fair zu sein, der Rat des Autors war nicht “übergeben Sie keine konstanten Zeichenfolgen als printf‘s erstes Argument”, war es “Strings nicht übergeben ohne % wie printf‘s erstes Argument.”

    – Steve Summit

    8. Juli 2015 um 14:07 Uhr


  • literally from the C programming book of Genesis. Anyone deprecating that usage is being quite offensively heretical – Sie haben K&R in den letzten Jahren nicht wirklich gelesen. Es gibt eine Menge Ratschläge und Programmierstile, die heutzutage nicht nur veraltet, sondern einfach nur schlechte Praxis sind.

    – Voo

    9. Juli 2015 um 13:43 Uhr

  • @Voo: Nun, sagen wir einfach, dass nicht alles, was als schlechte Praxis angesehen wird, auch so ist eigentlich schlechte Praxis. (Der Ratschlag „Niemals Klartext verwenden int“ fällt mir ein.)

    – Steve Gipfel

    9. Juli 2015 um 15:22 Uhr

  • @Steve Ich habe keine Ahnung, wo du das gehört hast, aber das ist sicherlich nicht die Art von schlechter (schlechter?) Praxis, über die wir dort sprechen. Verstehen Sie mich nicht falsch, für die Zeit war der Code völlig in Ordnung, aber Sie wollen k&r heutzutage wirklich nicht mehr als eine historische Notiz betrachten. “It’s in k&r” ist heutzutage einfach kein Indikator für gute Qualität, das ist alles

    – Voo

    9. Juli 2015 um 17:09 Uhr


Benutzeravatar von mernst
merst

Berufung printf mit wörtlichen Formatzeichenfolgen ist sicher und effizient, und es gibt Tools, die Sie automatisch warnen, wenn Ihr Aufruf von printf mit vom Benutzer bereitgestellten Formatzeichenfolgen ist unsicher.

Die schwersten Angriffe auf printf nutzen Sie die %n Formatbezeichner. Im Gegensatz zu allen anderen Formatbezeichnern, z %d, %n schreibt tatsächlich einen Wert an eine Speicheradresse, die in einem der Formatargumente angegeben ist. Dies bedeutet, dass ein Angreifer den Speicher überschreiben und so möglicherweise die Kontrolle über Ihr Programm übernehmen kann. Wikipedia
bietet mehr Details.

Wenn Sie anrufen printf Mit einer wörtlichen Formatzeichenfolge kann ein Angreifer a nicht einschleichen %n in Ihren Formatstring und Sie sind somit auf der sicheren Seite. Tatsächlich ändert gcc Ihren Anruf in printf in einen Anruf zu putsalso gibt es buchstäblich keinen Unterschied (testen Sie dies durch Ausführen gcc -O3 -S).

Wenn Sie anrufen printf Mit einer vom Benutzer bereitgestellten Formatzeichenfolge kann ein Angreifer möglicherweise a %n in Ihren Formatstring und übernehmen Sie die Kontrolle über Ihr Programm. Ihr Compiler wird Sie normalerweise warnen, dass sein Compiler unsicher ist, siehe
-Wformat-security. Es gibt auch fortgeschrittenere Tools, die sicherstellen, dass ein Aufruf von printf ist sogar mit vom Benutzer bereitgestellten Formatzeichenfolgen sicher, und sie könnten sogar überprüfen, ob Sie die richtige Anzahl und den richtigen Typ von Argumenten übergeben
printf. Beispielsweise gibt es für Java Fehleranfällig von Google
und die Checker-Framework.

  • because printf's first argument is always a constant string Ich bin mir nicht ganz sicher, was du damit meinst.

    – Sebastian Mach

    8. Juli 2015 um 13:57 Uhr

  • Wie ich sagte, "He has %d cherries\n" ist eine konstante Zeichenfolge, was bedeutet, dass es sich um eine Konstante zur Kompilierzeit handelt. Aber um fair zu sein, der Rat des Autors war nicht “übergeben Sie keine konstanten Zeichenfolgen als printf‘s erstes Argument”, war es “Strings nicht übergeben ohne % wie printf‘s erstes Argument.”

    – Steve Summit

    8. Juli 2015 um 14:07 Uhr


  • literally from the C programming book of Genesis. Anyone deprecating that usage is being quite offensively heretical – Sie haben K&R in den letzten Jahren nicht wirklich gelesen. Es gibt eine Menge Ratschläge und Programmierstile, die heutzutage nicht nur veraltet, sondern einfach nur schlechte Praxis sind.

    – Voo

    9. Juli 2015 um 13:43 Uhr

  • @Voo: Nun, sagen wir einfach, dass nicht alles, was als schlechte Praxis angesehen wird, auch so ist eigentlich schlechte Praxis. (Der Ratschlag „Niemals Klartext verwenden int“ fällt mir ein.)

    – Steve Gipfel

    9. Juli 2015 um 15:22 Uhr

  • @Steve Ich habe keine Ahnung, wo du das gehört hast, aber das ist sicherlich nicht die Art von schlechter (schlechter?) Praxis, über die wir dort sprechen. Verstehen Sie mich nicht falsch, für die Zeit war der Code völlig in Ordnung, aber Sie wollen k&r heutzutage wirklich nicht mehr als eine historische Notiz betrachten. “It’s in k&r” ist heutzutage einfach kein Indikator für gute Qualität, das ist alles

    – Voo

    9. Juli 2015 um 17:09 Uhr


Benutzeravatar von Supercat
Superkatze

Ein ziemlich unangenehmer Aspekt von printf ist, dass selbst auf Plattformen, auf denen das Lesen des Streuspeichers nur begrenzten (und akzeptablen) Schaden anrichten könnte, eines der Formatierungszeichen, %n, bewirkt, dass das nächste Argument als Zeiger auf eine schreibbare Ganzzahl interpretiert wird, und bewirkt, dass die Anzahl der bisher ausgegebenen Zeichen in der dadurch identifizierten Variablen gespeichert wird. Ich habe diese Funktion nie selbst verwendet, und manchmal verwende ich leichte Methoden im printf-Stil, die ich geschrieben habe, um nur die Funktionen einzuschließen, die ich tatsächlich verwende (und diese oder ähnliches nicht einzuschließen), sondern die erhaltenen Standardzeichenfolgen von printf-Funktionen zu füttern aus nicht vertrauenswürdigen Quellen können Sicherheitslücken offenlegen, die über die Fähigkeit hinausgehen, beliebigen Speicher zu lesen.

1421720cookie-checkWarum ist printf mit einem einzigen Argument (ohne Konvertierungsbezeichner) veraltet?

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

Privacy policy