Warum wird strncpy nicht mit null beendet?

Lesezeit: 8 Minuten

Benutzeravatar von Timothy Pratley
Timothy Pratley

strncpy() schützt angeblich vor Pufferüberläufen. Aber wenn es einen Überlauf ohne Nullterminierung verhindert, wird aller Wahrscheinlichkeit nach eine nachfolgende Zeichenfolgenoperation überlaufen. Um mich dagegen zu schützen, mache ich Folgendes:

strncpy( dest, src, LEN );
dest[LEN - 1] = '\0';

man strncpy gibt:

Das strncpy() Funktion ist ähnlich, außer dass nicht mehr als n Byte von src werden kopiert. Also, wenn unter den ersten kein Null-Byte ist n Byte von srcwird das Ergebnis nicht nullterminiert.

Ohne null etwas scheinbar Unschuldiges zu beenden wie:

   printf( "FOO: %s\n", dest );

…könnte abstürzen.


Gibt es bessere und sicherere Alternativen zu strncpy()?

  • Beachten Sie, dass auf MacOS X (BSD) die Manpage sagt (von ‘extern char *strncpy(char * restrict s1, const char * restrict s2, size_t n);‘): Die Funktion strncpy() kopiert höchstens n Zeichen von s2 nach s1. Wenn s2 weniger als n Zeichen lang ist, wird der Rest von s1 mit `\0’-Zeichen aufgefüllt. Andernfalls wird s1 nicht beendet.

    – Jonathan Leffler

    21. September 2009 um 21:31 Uhr

  • Sollte es nicht Ziel sein[LEN-1] = ‘\0’; ?

    – codeObserver

    18. Mai 2011 um 17:29 Uhr

  • So würde ich denken, dass wir eine Kopie der Zeichenfolge erstellen würden: int LEN = src.len; str* Ziel = neues Zeichen[LEN+1]; strncpy( Ziel, Quelle, LEN); Ziel[LEN] = ‘\0’;

    – codeObserver

    18. Mai 2011 um 17:30 Uhr


  • Immer Memset für Zielzeichenfolge verwenden ist der sicherste Ansatz, wenn Sie sicher sind, dass die Größe der Zeichenfolge die Länge des Zielpuffers nicht überschreitet.

    – koolvcvc

    4. Juli 2014 um 12:02 Uhr

  • Schreiben Sie Ihre eigene Funktion, ich denke nicht, dass das eine schwierige Aufgabe sein sollte

    – Megharaj

    30. März 2017 um 11:26 Uhr

Benutzeravatar von Ernelli
Ernelli

strncpy() ist nicht als Safer gedacht strcpy()es soll verwendet werden, um eine Saite in die Mitte einer anderen einzufügen.

Alle diese “sicheren” String-Handling-Funktionen wie z snprintf() und vsnprintf() sind Korrekturen, die in späteren Standards hinzugefügt wurden, um Pufferüberlauf-Exploits usw.

Wikipedia erwähnt strncat() als Alternative zum Schreiben eines eigenen Safes strncpy():

*dst="\0";
strncat(dst, src, LEN);

BEARBEITEN

das habe ich vermisst strncat() überschreitet LEN Zeichen, wenn null die Zeichenfolge beendet, wenn sie länger oder gleich LEN Zeichen ist.

Wie auch immer, der Punkt der Verwendung strncat() Anstelle einer selbst entwickelten Lösung wie z memcpy(..., strlen(...))/Was ist das die Implementierung von strncat() kann in der Bibliothek ziel-/plattformoptimiert sein.

Natürlich müssen Sie überprüfen, ob dst mindestens das Nullzeichen enthält, also die korrekte Verwendung von strncat() wäre so etwas wie:

if (LEN) {
    *dst="\0"; strncat(dst, src, LEN-1);
}

Das gebe ich auch zu strncpy() ist nicht sehr nützlich, um einen Teilstring in einen anderen String zu kopieren, wenn die Quelle kürzer als n Zeichen ist, wird der Zielstring abgeschnitten.

  • “Es soll verwendet werden, um eine Zeichenfolge in die Mitte einer anderen einzufügen” – nein, es soll eine Zeichenfolge in ein Feld mit fester Breite schreiben, z. B. in einen Verzeichniseintrag. Aus diesem Grund wird der Ausgabepuffer mit NUL aufgefüllt, wenn (und nur wenn) die Quellzeichenfolge zu kurz ist.

    – Steve Jessop

    21. September 2009 um 11:19 Uhr

  • Wie macht das Setzen von *dst=’\0′ dies sicherer? Es hat immer noch das ursprüngliche Problem, dass Sie über das Ende des Zielpuffers hinaus schreiben können.

    – Adam Liss

    21. September 2009 um 11:22 Uhr

  • klingt gut, aber sollte es nicht strncat(dst,src,LEN-1) sein, da es ein zusätzliches Zeichen schreiben wird?

    – Timothy Pratley

    21. September 2009 um 11:37 Uhr

  • @Jonathan: Eigentlich sicher wäre ein Datentyp, der einen Zeiger auf einen Zeichenpuffer mit der Länge dieses Puffers kombiniert. Aber wir alle wissen, dass das nicht passieren wird. Persönlich bin ich all dieser Bemühungen knochenmüde, etwas, das von Natur aus unsicher ist (Programmierer versuchen, die Länge eines Puffers genau zu respektieren), einen Bruchteil sicherer zu machen. Es ist nicht so, dass wir derzeit 50 % zu viele Pufferüberläufe haben, also wenn wir nur die String-Verarbeitung um 50 % sicherer machen könnten, wäre alles in Ordnung 🙁

    – Steve Jessop

    21. September 2009 um 12:06 Uhr

  • +1, um den Müll nicht zu wiederholen, dass strncpy irgendwie eine sichere Version von strcpy ist – ersteres hat seine eigenen Probleme.

    – paxdiablo

    12. Februar 2013 um 7:28 Uhr

Benutzeravatar von Jonathan Leffler
Jonathan Leffler

Ursprünglich die 7. Ausgabe UNIX Dateisystem (siehe DIR(5)) hatte Verzeichniseinträge, die Dateinamen auf 14 Byte begrenzten; Jeder Eintrag in einem Verzeichnis bestand aus 2 Bytes für die Inode-Nummer plus 14 Bytes für den Namen, auf 14 Zeichen mit Null aufgefüllt, aber nicht unbedingt mit Null abgeschlossen. Das ist meine Überzeugung strncpy() wurde entwickelt, um mit diesen Verzeichnisstrukturen zu arbeiten – oder zumindest funktioniert es perfekt für diese Struktur.

In Betracht ziehen:

  • Ein Dateiname mit 14 Zeichen wurde nicht nullterminiert.
  • Wenn der Name kürzer als 14 Bytes war, wurde er mit Nullen auf die volle Länge (14 Bytes) aufgefüllt.

Genau das würde erreicht werden durch:

strncpy(inode->d_name, filename, 14);

So, strncpy() wurde ideal an seine ursprüngliche Nischenanwendung angepasst. Es ging nur zufällig darum, Überläufe von nullterminierten Strings zu verhindern.

(Beachten Sie, dass das Auffüllen mit Nullen bis zur Länge 14 kein ernsthafter Overhead ist – wenn die Länge des Puffers 4 KB beträgt und Sie nur 20 Zeichen sicher hineinkopieren möchten, dann sind die zusätzlichen 4075 Nullen ein ernsthafter Overkill und können leicht passieren zu quadratischem Verhalten führen, wenn Sie einem langen Puffer wiederholt Material hinzufügen.)

  • Diese spezielle Situation mag unklar sein, aber es ist nicht gerade ungewöhnlich, Datenstrukturen mit Zeichenfolgenfeldern fester Länge zu haben, die mit Nullen aufgefüllt, aber nicht mit Nullen terminiert sind. Wenn man Daten in einem festen Format speichert, ist das oft der effizienteste Weg, dies zu tun.

    – Superkatze

    20. November 2011 um 22:54 Uhr

Es gibt bereits Open-Source-Implementierungen wie strlcpy die sicher kopieren.

http://en.wikipedia.org/wiki/Strlcpy

In den Literaturhinweisen befinden sich Links zu den Quellen.

  • Ganz zu schweigen davon, tragbar, schnell und zuverlässig. Sie können es immer noch missbrauchen, aber das Risiko ist um Größenordnungen geringer. IMO, strncpy sollte veraltet sein und durch dieselbe Funktion namens dirnamecpy oder so ähnlich ersetzt werden. strncpy ist keine sichere Zeichenfolgenkopie und war es noch nie.

    Benutzer14554

    21. September 2009 um 23:38 Uhr

Benutzeravatar von Liran Orevi
Liran Orevi

Strncpy ist sicherer gegen Stapelüberlaufangriffe durch die Benutzer Ihres Programms, es schützt Sie nicht vor Fehlern Sie der Programmierer, z. B. das Drucken einer nicht nullterminierten Zeichenfolge, wie Sie es beschrieben haben.

Sie können einen Absturz aufgrund des von Ihnen beschriebenen Problems vermeiden, indem Sie die Anzahl der von printf gedruckten Zeichen begrenzen:

char my_string[10];
//other code here
printf("%.9s",my_string); //limit the number of chars to be printed to 9

Benutzeravatar von Adam Liss
Adam Liss

Einige neue Alternativen sind in ISO/IEC TR 24731 (Check https://buildsecurityin.us-cert.gov/daisy/bsi/articles/knowledge/coding/317-BSI.html zur Info). Die meisten dieser Funktionen verwenden einen zusätzlichen Parameter, der die maximale Länge der Zielvariablen angibt, sicherstellen, dass alle Zeichenfolgen nullterminiert sind und Namen haben, die auf enden _s (für “sicher” ?), um sie von ihren früheren “unsicheren” Versionen zu unterscheiden.1

Leider werden sie immer noch unterstützt und sind möglicherweise nicht mit Ihrem speziellen Toolset verfügbar. Spätere Versionen von Visual Studio geben Warnungen aus, wenn Sie die alten unsicheren Funktionen verwenden.

Wenn Ihre Werkzeuge nicht die neuen Funktionen unterstützen, sollte es ziemlich einfach sein, eigene Wrapper für die alten Funktionen zu erstellen. Hier ist ein Beispiel:

errCode_t strncpy_safe(char *sDst, size_t lenDst,
                       const char *sSrc, size_t count)
{
    // No NULLs allowed.
    if (sDst == NULL  ||  sSrc == NULL)
        return ERR_INVALID_ARGUMENT;

   // Validate buffer space.
   if (count >= lenDst)
        return ERR_BUFFER_OVERFLOW;

   // Copy and always null-terminate
   memcpy(sDst, sSrc, count);
   *(sDst + count) = '\0';

   return OK;
}

Sie können die Funktion an Ihre Bedürfnisse anpassen, um beispielsweise immer so viel wie möglich vom String zu kopieren, ohne dass es zu einem Überlauf kommt. Tatsächlich kann die VC++-Implementierung dies tun, wenn Sie bestehen _TRUNCATE als die count.


1Natürlich müssen Sie die Größe des Zielpuffers immer noch genau angeben: Wenn Sie einen 3-Zeichen-Puffer angeben, sagen Sie es strcpy_s() Es hat Platz für 25 Zeichen, Sie haben immer noch Probleme.

  • Sie können keine Funktion legal definieren, deren Name mit str * beginnt, dieser “Namespace” ist in C reserviert.

    – abschalten

    21. September 2009 um 11:28 Uhr

  • Aber das ISO C-Komitee kann es – und hat es getan. Siehe auch: stackoverflow.com/questions/372980/…

    – Jonathan Leffler

    21. September 2009 um 11:42 Uhr

  • @Jonathan: Vielen Dank für den Querverweis auf Ihre eigene Frage, die viele zusätzliche hilfreiche Informationen enthält.

    – Adam Liss

    21. September 2009 um 12:08 Uhr

Benutzeravatar von alex tingle
Alex Kribbeln

Verwenden strlcpy()hier angegeben: http://www.courtesan.com/todd/papers/strlcpy.html

Wenn Ihre libc keine Implementierung hat, versuchen Sie es mit dieser:

size_t strlcpy(char* dst, const char* src, size_t bufsize)
{
  size_t srclen =strlen(src);
  size_t result =srclen; /* Result is always the length of the src string */
  if(bufsize>0)
  {
    if(srclen>=bufsize)
       srclen=bufsize-1;
    if(srclen>0)
       memcpy(dst,src,srclen);
    dst[srclen]='\0';
  }
  return result;
}

(Geschrieben von mir im Jahr 2004 – der Gemeinfreiheit gewidmet.)

  • Sie können keine Funktion legal definieren, deren Name mit str * beginnt, dieser “Namespace” ist in C reserviert.

    – abschalten

    21. September 2009 um 11:28 Uhr

  • Aber das ISO C-Komitee kann es – und hat es getan. Siehe auch: stackoverflow.com/questions/372980/…

    – Jonathan Leffler

    21. September 2009 um 11:42 Uhr

  • @Jonathan: Vielen Dank für den Querverweis auf Ihre eigene Frage, die viele zusätzliche hilfreiche Informationen enthält.

    – Adam Liss

    21. September 2009 um 12:08 Uhr

Benutzeravatar von Christoph
Christoph

Anstatt von strncpy()Du könntest benutzen

snprintf(buffer, BUFFER_SIZE, "%s", src);

Hier ist ein Einzeiler, der höchstens kopiert size-1 Nicht-Null-Zeichen von src zu dest und fügt ein Null-Terminator hinzu:

static inline void cpystr(char *dest, const char *src, size_t size)
{ if(size) while((*dest++ = --size ? *src++ : 0)); }

  • Wir verwenden Makroäquivalente zu snprintf(buffer, sizeof(buffer), "%s", src). Funktioniert gut, solange Sie daran denken, es niemals für char * -Ziele zu verwenden

    – che

    1. Februar 2013 um 8:30 Uhr

1419810cookie-checkWarum wird strncpy nicht mit null beendet?

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

Privacy policy