Warum werden strlcpy und strlcat als unsicher angesehen?

79

Ich verstehe das strlcpyund strlcatwurde als sicherer Ersatz für strncpyund konzipiert strncat. Einige Menschen sind jedoch immer noch der Meinung, dass sie unsicher sind und einfach eine andere Art von Problem verursachen .

Kann jemand ein Beispiel geben, wie die Verwendung von strlcpyoder strlcat(dh einer Funktion, deren Zeichenfolgen immer null sind) zu Sicherheitsproblemen führen kann?

Ulrich Drepper und James Antill geben an, dass dies wahr ist, geben jedoch niemals Beispiele oder klären diesen Punkt.

Anonym
quelle

Antworten:

111

Erstens war strlcpyes nie als sichere Version von gedacht strncpy(und strncpywar auch nie als sichere Version von gedacht strcpy). Diese beiden Funktionen sind völlig unabhängig voneinander. strncpyist eine Funktion, die überhaupt keine Beziehung zu C-Strings (dh nullterminierten Strings) hat. Die Tatsache, dass der str...Name das Präfix enthält, ist nur ein historischer Fehler. Die Geschichte und der Zweck von strncpyist bekannt und gut dokumentiert. Dies ist eine Funktion, die für die Arbeit mit sogenannten Zeichenfolgen mit fester Breite (nicht mit C-Zeichenfolgen) erstellt wurde, die in einigen historischen Versionen des Unix-Dateisystems verwendet werden. Einige Programmierer werden heute durch seinen Namen verwirrt und nehmen an, dass dies strncpyirgendwie als C-String-Kopierfunktion mit begrenzter Länge dienen soll (ein "sicheres" Geschwister vonstrcpy), was in Wirklichkeit völliger Unsinn ist und zu schlechter Programmierpraxis führt. Die C-Standardbibliothek in ihrer aktuellen Form hat keinerlei Funktion zum Kopieren von C-Strings mit begrenzter Länge. Hier strlcpypasst es hinein. Dies ist in der strlcpyTat eine echte Kopierfunktion mit begrenzter Länge, die für die Arbeit mit C-Strings entwickelt wurde. strlcpymacht alles richtig, was eine Kopierfunktion mit begrenzter Länge tun sollte. Die einzige Kritik, die man darauf richten kann, ist, dass es leider kein Standard ist.

Zweitens ist es strncatin der Tat eine Funktion, die mit C-Strings arbeitet und eine Verkettung mit begrenzter Länge ausführt (es ist in der Tat ein "sicheres" Geschwister von strcat). Um diese Funktion richtig zu verwenden, muss der Programmierer besondere Sorgfalt walten lassen, da der Größenparameter, den diese Funktion akzeptiert, nicht wirklich die Größe des Puffers ist, der das Ergebnis empfängt, sondern die Größe seines verbleibenden Teils (auch das Abschlusszeichen) wird implizit gezählt). Dies kann verwirrend sein, da der Programmierer, um diese Größe an die Größe des Puffers zu binden, daran denken muss, einige zusätzliche Berechnungen durchzuführen, die häufig zur Kritik des Puffers verwendet werden strncat.strlcatkümmert sich um diese Probleme und ändert die Schnittstelle so, dass keine zusätzlichen Berechnungen erforderlich sind (zumindest im aufrufenden Code). Die einzige Grundlage, auf der ich dies kritisieren kann, ist, dass die Funktion nicht Standard ist. Funktionen aus der strcatGruppe werden im professionellen Code aufgrund der eingeschränkten Verwendbarkeit der Idee der Rescan-basierten String-Verkettung nur selten verwendet.

Wie diese Funktionen zu Sicherheitsproblemen führen können ... können sie einfach nicht. Sie können nicht in größerem Maße zu Sicherheitsproblemen führen, als die C-Sprache selbst "zu Sicherheitsproblemen führen kann". Sie sehen, es gab eine ganze Weile ein starkes Gefühl, dass sich die C ++ - Sprache in Richtung einer seltsamen Java-Variante entwickeln muss. Dieses Gefühl wirkt sich manchmal auch auf den Bereich der C-Sprache aus, was zu einer eher ahnungslosen und erzwungenen Kritik an C-Sprachmerkmalen und den Merkmalen der C-Standardbibliothek führt. Ich vermute, dass wir uns auch in diesem Fall mit so etwas befassen könnten, obwohl ich sicherlich hoffe, dass die Dinge nicht wirklich so schlimm sind.

Ameise
quelle
9
Ich stimme nicht ganz zu. Es wäre schön, wenn strlcpyund strlcatwürde eine Art Fehlerbedingung melden, wenn sie gegen die Größenbeschränkung des Zielpuffers stoßen würden. Obwohl Sie die zurückgegebene Länge überprüfen können, um dies zu testen, ist dies nicht offensichtlich. Aber ich denke, das ist eine kleine Kritik. Das Argument "Sie fördern die Verwendung von C-Strings und sind daher schlecht" ist albern.
Omnifarious
7
"Wie diese Funktionen zu Sicherheitsproblemen führen können" - Ich denke, das Problem hier ist, dass einige C-Funktionen schwieriger korrekt zu verwenden sind als andere. Einige Menschen glauben fälschlicherweise, dass es eine spezielle Schwierigkeitsschwelle gibt, unterhalb derer eine Funktion "sicher" und oberhalb derer sie "unsicher" ist. Solche Leute sind auch in der Regel von der Überzeugung , dass strcpyüber den Schwellenwert liegt und damit „unsicher“, und ihre bevorzugte String-Kopierfunktion (ob es strlcpy, strcpy_soder auch strncpy) unter dem Schwellenwert liegt und damit „sicher“.
Steve Jessop
31
Es gibt viele Gründe, strlcpy / strlcat nicht zu mögen, aber Sie geben keinen an. Die Diskussion über C ++ und Java ist irrelevant. Diese Antwort ist für das Thema, zu dem die Frage tatsächlich gestellt wurde, einfach nicht hilfreich.
John Ripley
24
@ John Ripley: Erstens "sage ich keinen von ihnen", nur weil mir keine Gründe für Abneigungen bekannt sind strlcpy/strlcat. Man könnte das allgemeine Konzept der nullterminierten Zeichenfolge "nicht mögen", aber darum geht es bei der Frage nicht. Wenn Sie "viele Gründe kennen, die Sie nicht mögen strlcpy/strlcat", sollten Sie wahrscheinlich Ihre eigene Antwort schreiben, anstatt zu erwarten, dass ich die Gedanken eines anderen lesen kann.
Am
8
@ John Ripley: Zweitens bezog sich die Frage speziell auf einige angebliche "Sicherheitsprobleme" mit strlcpy/strlcat. Obwohl ich glaube zu verstehen, worum es geht, lehne ich es persönlich ab, dies als "Sicherheitsprobleme" im Bereich der traditionellen C-Sprache, wie ich sie kenne, anzuerkennen. Das habe ich in meiner Antwort angegeben.
AnT
31

Ulrichs Kritik basiert auf der Idee, dass eine vom Programm nicht erkannte Zeichenfolgenabschneidung durch falsche Logik zu Sicherheitsproblemen führen kann. Um sicher zu sein, müssen Sie daher auf Kürzungen prüfen. Um dies für eine Zeichenfolgenverkettung zu tun, müssen Sie eine Überprüfung in der folgenden Richtung durchführen:

if (destlen + sourcelen > dest_maxlen)
{
    /* Bug out */
}

Nun strlcattut effektiv diese Prüfung, wenn der Programmierer das Ergebnis zu überprüfen , erinnert sich - so Sie können es sicher verwenden:

if (strlcat(dest, source, dest_bufferlen) >= dest_bufferlen)
{
    /* Bug out */
}

Ulrichs Argument ist, dass Sie, da Sie destlenund sourcelenum sie haben müssen (oder sie neu berechnen müssen, was strlcateffektiv ist), genauso gut das effizientere verwenden memcpykönnen:

if (destlen + sourcelen > dest_maxlen)
{
    goto error_out;
}
memcpy(dest + destlen, source, sourcelen + 1);
destlen += sourcelen;

(Im obigen Code dest_maxlenist die maximale Länge der Zeichenfolge, in der gespeichert werden kann dest- eine weniger als die Größe des destPuffers. dest_bufferlenIst die volle Größe der dest buffer).

caf
quelle
13
Die Lesbarkeit von Dreppers Code ist schlecht. Mit strlcpy (oder einer beliebigen str-Funktion) weiß ich direkt, dass ich eine 0-terminierte C-Zeichenfolge kopiere. Damit memcpykann jede Art von Speicher sein und ich habe eine zusätzliche Dimension zu überprüfen, wenn ich versuche, den Code zu verstehen. Ich hatte eine Legacy-App zum Debuggen, bei der alles mit memcpy erledigt wurde. Es war eine echte PITA, die korrigiert werden musste. Nach dem Portieren auf eine dedizierte String-Funktion ist das Lesen viel einfacher (und schneller, da viele unnötige strlenElemente entfernt werden könnten).
Patrick Schlüter
3
@domen: Da die zu kopierende Größe bereits bekannt ist, memcpy()ist dies ausreichend (und möglicherweise effizienter als strcpy()).
Café
1
Nun, es ist verwirrend, es in String-Operationen zu haben. Und soweit ich weiß, hängt Effizienz von der Implementierung ab und ist nicht standardisiert.
Domen
8
@domen: memcpy() ist eine String-Operation - sie wird schließlich in deklariert <string.h>.
Café
2
@domen Ich stimme zu, dass es Verwirrung geben kann, aber die Realität ist, dass die Arbeit mit C-Strings sowieso ziemlich viel mit rohem Speicher funktioniert. Wahrscheinlich wären wir alle besser dran, wenn die Leute einfach aufhören würden, C als "Zeichenfolgen" (im Unterschied zu anderen zusammenhängenden Speicherblöcken) zu betrachten.
mtraceur
19

Wenn Leute sagen, " strcpy()ist gefährlich, verwenden Sie strncpy()stattdessen" (oder ähnliche Aussagen über strcat()usw., aber ich werde strcpy()hier als meinen Fokus verwenden), bedeuten sie, dass es keine Grenzen beim Einchecken gibt strcpy(). Eine zu lange Zeichenfolge führt daher zu Pufferüberläufen. Sie sind richtig. Mit strncpy()in diesem Fall wird Pufferüberläufe zu verhindern.

Ich denke, strncpy()das behebt wirklich keine Fehler: Es löst ein Problem, das von einem guten Programmierer leicht vermieden werden kann.

Als C - Programmierer, Sie müssen die Zielgröße kennen , bevor Sie Zeichenfolgen zu kopieren versuchen. Dies ist auch die Annahme in strncpy()und strlcpy()'s letzten Parametern: Sie geben ihnen diese Größe. Sie können auch die Quellgröße kennen, bevor Sie Zeichenfolgen kopieren. Wenn das Ziel nicht groß genug ist, rufen Sie nicht anstrcpy() . Ordnen Sie den Puffer entweder neu zu oder tun Sie etwas anderes.

Warum mag ich nicht strncpy()?

  • strncpy() ist in den meisten Fällen eine schlechte Lösung: Ihre Zeichenfolge wird ohne vorherige Ankündigung abgeschnitten. Ich würde lieber zusätzlichen Code schreiben, um dies selbst herauszufinden, und dann die Vorgehensweise ausführen, die ich ausführen möchte, anstatt eine Funktion entscheiden zu lassen mir über was zu tun ist.
  • strncpy()ist sehr ineffizient. Es schreibt in jedes Byte im Zielpuffer. Sie brauchen diese Tausenden '\0'am Ende Ihres Ziels nicht.
  • Es wird keine Beendigung geschrieben, '\0'wenn das Ziel nicht groß genug ist. Sie müssen dies also trotzdem selbst tun. Die Komplexität ist die Mühe nicht wert.

Nun kommen wir zu strlcpy(). Die Änderungen von strncpy()machen es besser, aber ich bin nicht sicher, ob das spezifische Verhalten von strl*ihre Existenz rechtfertigt: Sie sind viel zu spezifisch. Sie müssen noch die Zielgröße kennen. Es ist effizienter als strncpy()weil es nicht unbedingt in jedes Byte im Ziel schreibt. Aber es löst ein Problem, das gelöst werden kann, indem man : *((char *)mempcpy(dst, src, n)) = 0;.

Ich glaube nicht, dass jemand das sagt strlcpy()oder strlcat()zu Sicherheitsproblemen führen kann, was er (und ich) sagen, dass sie zu Fehlern führen können, zum Beispiel wenn Sie erwarten, dass die vollständige Zeichenfolge anstelle eines Teils davon geschrieben wird.

Das Hauptproblem hierbei ist: Wie viele Bytes müssen kopiert werden? Der Programmierer muss dies wissen und wenn er es nicht tut strncpy()oder strlcpy()nicht retten wird.

strlcpy()und strlcat()sind nicht Standard, weder ISO C noch POSIX. Ihre Verwendung in tragbaren Programmen ist daher unmöglich. Tatsächlich strlcat()gibt es zwei verschiedene Varianten: Die Solaris-Implementierung unterscheidet sich von den anderen für Randfälle mit der Länge 0. Dies macht sie noch weniger nützlich als sonst.

Alok Singhal
quelle
3
strlcpyist schneller als memcpyauf vielen Architekturen, insbesondere wenn die memcpyKopien unnötige nachfolgende Daten kopieren. strlcpyGibt auch zurück, wie viele Daten Sie verpasst haben, wodurch Sie möglicherweise schneller und mit weniger Code wiederhergestellt werden können.
1
@jbcreix: Mein Punkt ist, dass es keine Daten geben sollte, die übersehen werden sollten, und nin meinem memcpy-Aufruf ist die genaue Anzahl der zu schreibenden Bytes angegeben, sodass die Effizienz auch kein so großes Problem darstellt.
Alok Singhal
5
Und wie bekommst du das n? Das einzige n, das Sie im Voraus kennen können, ist die Puffergröße. Natürlich , wenn Sie vorschlagen re Umsetzung strlcpyjedes Mal , wenn Sie es brauchen verwenden memcpyund strlendas ist auch in Ordnung, aber warum dann beim Anhalten strlcpy, nicht wahr brauchen eine memcpyFunktion, können Sie entweder die Bytes einzeln kopieren. Die Referenzimplementierung durchläuft die Daten im Normalfall nur einmal, was für die meisten Architekturen besser ist. Aber selbst wenn die beste Implementierung strlen+ verwendet memcpy, ist dies immer noch kein Grund, einen sicheren Strcpy nicht immer wieder neu implementieren zu müssen.
1
Alok, strlcpywenn Sie im guten Fall (der die Mehrheit sein sollte) nur einmal über die Eingabezeichenfolge streichen und im schlechten Fall zweimal, mit Ihrem strlen+ memcpygehen Sie immer zweimal darüber. Ob es in der Praxis einen Unterschied macht, ist eine andere Geschichte.
Patrick Schlüter
4
* ((char *) mempcpy (dst, src, n)) = 0; - Richtig - Ja, offensichtlich richtig, Nein.
Fehler
12

Ich denke, Ulrich und andere denken, dass es ein falsches Gefühl der Sicherheit geben wird. Das versehentliche Abschneiden von Zeichenfolgen kann Auswirkungen auf die Sicherheit anderer Teile des Codes haben (wenn beispielsweise ein Dateisystempfad abgeschnitten wird, führt das Programm möglicherweise keine Vorgänge für die beabsichtigte Datei aus).

Jamesdlin
quelle
8
Beispielsweise kann ein E-Mail-Client den Dateinamen eines E-Mail-Anhangs von malware.exe.jpgbis abschneiden malware.exe.
Chris Peterson
1
@ChrisPeterson Aus diesem Grund überprüft ein guter Entwickler immer die Rückgabewerte, um bei strl * -Funktionen festzustellen, ob die Daten abgeschnitten wurden, und handelt entsprechend.
Tom Lint
"Ulrich und andere denken, dass es ein falsches Gefühl der Sicherheit geben wird ..." - Lol ... währenddessen treten Ulrich und seine Freunde regelmäßig bei BugTraq und Full Disclosure für ihre einmaligen Auftritte auf. Sie sollten die sichereren Funktionen verwenden und die meisten ihrer Probleme vermeiden. Dann können sie anderen sagen, wie sie sichereren Code schreiben sollen ...
jww
7

Es gibt zwei "Probleme" im Zusammenhang mit der Verwendung von strl-Funktionen:

  1. Sie müssen die Rückgabewerte überprüfen, um ein Abschneiden zu vermeiden.

Die c1x Standard Draft Writer und Drepper argumentieren, dass Programmierer den Rückgabewert nicht überprüfen werden. Drepper sagt, wir sollten irgendwie die Länge kennen und memcpy verwenden und String-Funktionen insgesamt vermeiden. Das Standardkomitee argumentiert, dass der sichere strcpy beim Abschneiden ungleich Null zurückgeben sollte, sofern das _TRUNCATEFlag nichts anderes angibt . Die Idee ist, dass Leute eher if (strncpy_s (...)) verwenden.

  1. Kann nicht für Nicht-Strings verwendet werden.

Einige Leute denken, dass String-Funktionen niemals abstürzen sollten, selbst wenn falsche Daten eingegeben werden. Dies betrifft Standardfunktionen wie strlen, die unter normalen Bedingungen fehlerhaft sind. Der neue Standard wird viele solcher Funktionen enthalten. Die Schecks haben natürlich eine Leistungsstrafe.

Der Vorteil der vorgeschlagenen Standardfunktionen besteht darin, dass Sie wissen, wie viele Daten Sie mit strl- Funktionen verpasst haben .


quelle
Beachten Sie, dass dies strncpy_skeine sichere Version von ist, strncpysondern im Grunde ein strlcpyErsatz.
6

Ich glaube nicht , strlcpyund strlcatsind betrachten unsicher oder es am wenigsten es ist nicht der Grund , warum sie nicht in glibc enthalten - schließlich, glibc enthält Strncpy und sogar strcpy.

Die Kritik war, dass sie angeblich ineffizient und nicht unsicher sind .

Laut dem Secure Portability- Papier von Damien Miller:

Die API strlcpy und strlcat überprüft die Grenzen des Zielpuffers ordnungsgemäß, beendet sie in allen Fällen nicht und gibt die Länge der Quellzeichenfolge zurück, sodass das Abschneiden erkannt werden kann. Diese API wurde von den meisten modernen Betriebssystemen und vielen eigenständigen Softwarepaketen übernommen, darunter OpenBSD (woher sie stammt), Sun Solaris, FreeBSD, NetBSD, den Linux-Kernel, rsync und das GNOME-Projekt. Die bemerkenswerte Ausnahme ist die GNU-Standard-C-Bibliothek glibc [12], deren Betreuer sich entschieden weigert, diese verbesserten APIs aufzunehmen, und sie als „schrecklich ineffizienten BSD-Mist“ bezeichnet.[4], obwohl zuvor nachgewiesen wurde, dass sie schneller sind als die APIs, die sie ersetzen [13]. Infolgedessen behalten über 100 der im OpenBSD-Portsbaum enthaltenen Softwarepakete ihre eigenen strlcpy- und / oder strlcat-Ersetzungen oder gleichwertigen APIs bei - kein idealer Zustand.

Aus diesem Grund sind sie in glibc nicht verfügbar, aber es stimmt nicht, dass sie unter Linux nicht verfügbar sind. Sie sind unter Linux in libbsd verfügbar:

Sie sind in Debian und Ubuntu und anderen Distributionen verpackt. Sie können auch einfach eine Kopie nehmen und in Ihrem Projekt verwenden - es ist kurz und unter einer zulässigen Lizenz:

rsp
quelle
3

Sicherheit ist kein Boolescher Wert. C-Funktionen sind nicht vollständig "sicher" oder "unsicher", "sicher" oder "unsicher". Bei falscher Verwendung kann eine einfache Zuweisungsoperation in C "unsicher" sein. strlcpy () und strlcat () können sicher verwendet werden, ebenso wie strcpy () und strcat () sicher verwendet werden können, wenn der Programmierer die erforderlichen Zusicherungen für eine korrekte Verwendung bietet.

Der Hauptpunkt bei all diesen C-String-Funktionen, Standard und nicht so Standard, ist das Niveau, bis zu dem sie die sichere Verwendung erleichtern . strcpy () und strcat () sind nicht trivial sicher zu verwenden; Dies wird durch die Häufigkeit bewiesen, mit der C-Programmierer im Laufe der Jahre Fehler gemacht haben und böse Sicherheitslücken und Exploits aufgetreten sind. strlcpy () und strlcat () sowie strncpy () und strncat (), strncpy_s () und strncat_s () sind etwas einfacher sicher zu verwenden, aber dennoch nicht trivial. Sind sie unsicher / unsicher? Bei falscher Verwendung ist nicht mehr als memcpy ().

rswindell
quelle