Sammeln Sie Fehler in den Kopierfunktionen

memcpy

Ich habe mehrmals bemerkt, dass Programmierer Fehler bei einfachen Datenkopierfunktionen machen. Dieses Thema wird in Zukunft viel mehr Zeit benötigen, um Material zu studieren und auszuwählen, um einen gründlichen Artikel zu schreiben. Aber ich wollte ein paar Beispiele nennen, die mir kürzlich aufgefallen sind.

Das Baadera-Meinhof-Phänomen? Nein, ich denke nicht so


Als Mitglied des PVS-Studio-Teams stoße ich auf eine Vielzahl von Fehlern, die wir in verschiedenen Projekten entdecken. Wie DevRel - ich rede gerne darüber :). Heute habe ich beschlossen, über falsch implementierte Datenkopierfunktionen zu sprechen.

Ich bin mehr als einmal auf solche erfolglosen Funktionen gestoßen. Aber ich habe sie nicht ausgeschrieben, weil ich dem keine Bedeutung beigemessen habe. Da ich diesen Trend jedoch bemerkt habe, ist es Zeit, sie zu sammeln. Zunächst werde ich die letzten beiden festgestellten Fälle teilen.

Jemand könnte argumentieren, dass zwei Fälle - dies ist keine Regelmäßigkeit. Und das hat mich vielleicht nur deshalb auf sie aufmerksam gemacht, weil sie mich nach kurzer Zeit getroffen und das Baader-Meinhof- Phänomen ausgelöst haben .

Das Baader-Meinhof-Phänomen, auch die Illusion von Frequenz, ist eine kognitive Verzerrung, bei der kürzlich entdeckte Informationen, die nach kurzer Zeit wieder auftreten, als ungewöhnlich häufig wiederholt wahrgenommen werden.

Ich denke das ist nicht so. Ich hatte bereits die Erfahrung einer solchen Beobachtung über die Vergleichsfunktionen, die dann durch das gesammelte Material bestätigt wurde: "Das Böse lebt in den Vergleichsfunktionen ."

Okay, kommen wir zum Punkt. Die Einführung, um nur zwei Beispiele zu nennen, war bisher zu lang :).

Beispiel N1


In dem Artikel über die Zephyr RTOS-Verifizierung habe ich einen solchen erfolglosen Versuch beschrieben, das Analogon der strdup- Funktion zu implementieren :

static char *mntpt_prepare(char *mntpt)
{
  char *cpy_mntpt;

  cpy_mntpt = k_malloc(strlen(mntpt) + 1);
  if (cpy_mntpt) {
    ((u8_t *)mntpt)[strlen(mntpt)] = '\0';
    memcpy(cpy_mntpt, mntpt, strlen(mntpt));
  }
  return cpy_mntpt;
}

PVS-Studio Warnung: V575 [CWE-628] Die Funktion 'memcpy' kopiert nicht die gesamte Zeichenfolge. Verwenden Sie die Funktion 'strcpy / strcpy_s', um das Terminal null beizubehalten. shell.c 427

Der Analysator meldet, dass die memcpy- Funktion die Zeile kopiert, aber nicht das Terminal Null, und dies ist sehr verdächtig. Es scheint, dass dieses Terminal 0 hier kopiert wird:

((u8_t *)mntpt)[strlen(mntpt)] = '\0';

Nein, hier gibt es einen Tippfehler, aufgrund dessen die Klemme Null in sich selbst kopiert wird. Beachten Sie, dass beim Schreiben in das Array mntpt und nicht in cpy_mntpt . Infolgedessen gibt die Funktion mntpt_prepare eine Zeichenfolge zurück, die mit einem Terminal Null unvollständig ist.

Tatsächlich wollte der Programmierer so schreiben:

((u8_t *)cpy_mntpt)[strlen(mntpt)] = '\0';

Es ist nicht klar, warum der Code so verwirrend und nicht standardisiert geschrieben ist. Infolgedessen wurde ein schwerwiegender Fehler in einer kleinen und unkomplizierten Funktion gemacht. Dieser Code kann auf folgende Option vereinfacht werden:

static char *mntpt_prepare(char *mntpt)
{
  char *cpy_mntpt;

  cpy_mntpt = k_malloc(strlen(mntpt) + 1);
  if (cpy_mntpt) {
    strcpy(cpy_mntpt, mntpt);
  }
  return cpy_mntpt;
}

Beispiel N2


void myMemCpy(void *dest, void *src, size_t n) 
{ 
   char *csrc = (char *)src; 
   char *cdest = (char *)dest; 
   for (int i=0; i<n; i++) 
     cdest[i] = csrc[i]; 
}

Wir selbst haben diesen Code nicht mit PVS-Studio identifiziert, aber ich habe ihn versehentlich auf der StackOverflow-Website gefunden: C- und statische Code-Analyse: Ist dies sicherer als memcpy?

Wenn Sie diese Funktion jedoch mit dem PVS-Studio-Analysegerät überprüfen, wird er zu Recht Folgendes bemerken:

  • V104 Implizite Konvertierung von 'i' in memsize-Typ in einem arithmetischen Ausdruck: i <n test.cpp 26
  • V108 Falscher Indextyp: cdest [kein Memsize-Typ]. Verwenden Sie stattdessen den Typ memsize. test.cpp 27
  • V108 Falscher Indextyp: csrc [kein Memsize-Typ]. Verwenden Sie stattdessen den Typ memsize. test.cpp 27

In der Tat enthält dieser Code einen Fehler, wie in den Antworten auf StackOverflow angegeben. Sie können eine int- Variable nicht als Index verwenden . In einem 64-Bit-Programm ist die Variable int mit ziemlicher Sicherheit (wir berücksichtigen keine exotischen Architekturen) 32-Bit und die Funktion kann nicht mehr als INT_MAX-Bytes kopieren. Jene. nicht mehr als 2 Gigabyte.

Bei einem größeren zu kopierenden Puffer tritt ein Überlauf der Vorzeichenvariablen auf, was aus Sicht von C und C ++ ein undefiniertes Verhalten ist. Versuchen Sie übrigens nicht genau zu erraten, wie sich der Fehler manifestiert. Dies ist eigentlich ein schwieriges Thema, über das Sie im Artikel " Undefiniertes Verhalten ist näher als Sie denken " lesen können .

Es ist besonders lustig, dass dieser Code als Versuch erschien, eine Warnung aus dem Checkmarx-Analysator zu entfernen, die beim Aufruf der memcpy- Funktion aufgetreten ist . Der Programmierer hatte nichts Besseres als sein eigenes Fahrrad zu bauen. Und trotz der Einfachheit der Kopierfunktion stellte sich heraus, dass es immer noch falsch war. Das heißt, die Person hat es höchstwahrscheinlich noch schlimmer gemacht als sie war. Anstatt den Grund für die Warnung zu verstehen, maskierte er das Problem, indem er seine eigene Funktion schrieb (was den Analysator verwirrte). Außerdem wurde ein Fehler mit int für den Zähler hinzugefügt . Oh ja, ein solcher Code kann die Optimierung immer noch behindern. Es ist ineffizient, anstelle der effizienten optimierten memcpy- Funktion eigenen Code zu verwenden . Mach das nicht :)

Fazit


Nun, ich bin erst am Anfang der Reise und es wird wahrscheinlich mehr als ein Jahr dauern, bis ich Materialien für eine gründliche Veröffentlichung zu diesem Thema sammle. Eigentlich werde ich erst jetzt anfangen, solche Fälle aufzuschreiben. Vielen Dank für Ihre Aufmerksamkeit und schauen Sie, was den PVS-Studio- Analysator in Ihrem C / C ++ / C # / Java-Code interessant findet .



Wenn Sie diesen Artikel einem englischsprachigen Publikum zugänglich machen möchten, verwenden Sie bitte den Link zur Übersetzung: Andrey Karpov. Starten meiner Sammlung von Fehlern in Kopierfunktionen .

All Articles