Antworten auf Aufgaben vom PVS-Studio-Stand auf Konferenzen 2018-2019

Bild 2


Hallo! Trotz der Tatsache, dass die Konferenzsaison 2019 noch in vollem Gange ist, möchten wir auf die Aufgaben eingehen, die den Besuchern unseres Standes früher geboten wurden. Wir haben den Herbst 2019 mit einer Reihe neuer Aufgaben begonnen, sodass es bereits möglich ist, die Lösung alter Probleme für 2018 sowie für das erste Halbjahr 2019 zu veröffentlichen. Außerdem wurden viele davon aus zuvor veröffentlichten Artikeln entnommen, und die Aufgabenblätter enthielten einen Link oder einen QR-Code mit Informationen zum Artikel.

Wenn Sie an Konferenzen teilgenommen haben, bei denen wir mit einem Stand vertreten waren, haben Sie wahrscheinlich einige unserer Probleme gesehen oder sogar gelöst. Dies sind immer Codeausschnitte aus echten Open Source-Projekten in den Programmiersprachen C, C ++, C # oder Java. Der Code enthält Fehler, nach denen Besucher Ausschau halten sollten. Für die Lösung (oder nur eine Diskussion des Fehlers) geben wir Preise aus - Status auf dem Desktop, Schmuckstücke usw .:

Bild 4

Willst du das selbe Besuchen Sie uns auf den nächsten Konferenzen.

Übrigens enthalten die Artikel " Konferenzzeit! Zusammenfassung der Ergebnisse von 2018 " und " Konferenzen. Zwischenergebnisse für das erste Halbjahr 2019 " eine Beschreibung unserer Aktivitäten auf Konferenzen in diesem und im letzten Jahr.

Beginnen wir also mit dem Spiel „Finde einen Fehler im Code“. Betrachten wir zunächst die älteren Aufgaben für 2018, so werden wir die Gruppierung nach Programmiersprachen anwenden.

2018


C ++


Chrom Bug

static const int kDaysInMonth[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; bool ValidateDateTime(const DateTime& time) { if (time.year < 1 || time.year > 9999 || time.month < 1 || time.month > 12 || time.day < 1 || time.day > 31 || time.hour < 0 || time.hour > 23 || time.minute < 0 || time.minute > 59 || time.second < 0 || time.second > 59) { return false; } if (time.month == 2 && IsLeapYear(time.year)) { return time.month <= kDaysInMonth[time.month] + 1; } else { return time.month <= kDaysInMonth[time.month]; } } 

Die antwort
Vielleicht die "langwierigste" Aufgabe aus unserem Set. Wir haben vorgeschlagen, dass dieser Fehler im Chromium-Projekt von den Besuchern unseres Standes im Laufe des Jahres 2018 festgestellt wird. Es wurde auch in mehreren Berichten vorgestellt.

 if (time.month == 2 && IsLeapYear(time.year)) { return time.month <= kDaysInMonth[time.month] + 1; // <= day } else { return time.month <= kDaysInMonth[time.month]; // <= day } 

Der Body des letzten If-else- Blocks enthält Tippfehler im Rückgabewert. Anstelle von time.day gab der Programmierer zweimal fälschlicherweise time.month an . Dies hat dazu geführt, dass true immer zurückgegeben wird. Der Fehler ist im Artikel „ 31. Februar “ ausführlich beschrieben. Ein großartiges Beispiel für einen Fehler, der bei der Codeüberprüfung nicht leicht zu erkennen ist. Es ist auch ein gutes Beispiel für die Verwendung der Datenflussanalysetechnologie.

Unwirklicher Motorfehler

 bool VertInfluencedByActiveBone( FParticleEmitterInstance* Owner, USkeletalMeshComponent* InSkelMeshComponent, int32 InVertexIndex, int32* OutBoneIndex = NULL); void UParticleModuleLocationSkelVertSurface::Spawn(....) { .... int32 BoneIndex1, BoneIndex2, BoneIndex3; BoneIndex1 = BoneIndex2 = BoneIndex3 = INDEX_NONE; if(!VertInfluencedByActiveBone( Owner, SourceComponent, VertIndex[0], &BoneIndex1) && !VertInfluencedByActiveBone( Owner, SourceComponent, VertIndex[1], &BoneIndex2) && !VertInfluencedByActiveBone( Owner, SourceComponent, VertIndex[2]) &BoneIndex3) { .... } 

Die antwort
Als Erstes ist zu beachten, dass das letzte Argument für die Funktion VertInfluencedByActiveBone () einen Standardwert hat und möglicherweise nicht angegeben wird. Schauen Sie sich jetzt den if- Block an. Vereinfacht kann es wie folgt umgeschrieben werden:
 if (!foo(....) && !foo(....) && !foo(....) & arg) 

Jetzt ist klar, dass ein Fehler gemacht wurde. Aufgrund eines Tippfehlers wird der dritte Aufruf der Funktion VertInfluencedByActiveBone () mit drei statt vier Argumenten ausgeführt, und der Operator & wird auf das Ergebnis dieses Aufrufs angewendet (bitweises UND, links das Ergebnis der Funktion VertInfluencedByActiveBone () vom Typ bool , rechts die Ganzzahlvariable BoneIndex3 ). Der Code wird kompiliert. Korrigierte Version des Codes (Komma hinzugefügt, schließende Klammer an eine andere Stelle verschoben):

 if(!VertInfluencedByActiveBone( Owner, SourceComponent, VertIndex[0], &BoneIndex1) && !VertInfluencedByActiveBone( Owner, SourceComponent, VertIndex[1], &BoneIndex2) && !VertInfluencedByActiveBone( Owner, SourceComponent, VertIndex[2], &BoneIndex3)) 

Der Fehler, der ursprünglich im Artikel "Die lang erwartete Prüfung von Unreal Engine 4 " beschrieben wurde. Der Artikel trägt den Titel "Die schönsten der gefundenen Fehler". Ich stimme dieser Aussage zu.

Android-Fehler

 void TagMonitor::parseTagsToMonitor(String8 tagNames) { std::lock_guard<std::mutex> lock(mMonitorMutex); // Expand shorthands if (ssize_t idx = tagNames.find("3a") != -1) { ssize_t end = tagNames.find(",", idx); char* start = tagNames.lockBuffer(tagNames.size()); start[idx] = '\0'; .... } .... } 

Die antwort
Im Zustand des if- Blocks ist die Priorität der Operationen verwechselt. Der Code funktioniert nicht wie vom Programmierer beabsichtigt:

 if (ssize_t idx = (tagNames.find("3a") != -1)) 

Die Variable idx erhält die Werte 0 oder 1, und die Erfüllung der Bedingung hängt von diesem Wert ab, bei dem es sich um einen Fehler handelt. Korrigierte Version des Codes:

 ssize_t idx = tagNames.find("3a"); if (idx != -1) 

Fehler aus dem Artikel "Wir haben die Android-Quellcodes mit PVS-Studio überprüft, oder niemand ist perfekt ."

Und noch eine Aufgabe, um einen nicht trivialen Fehler in Android zu finden:

 typedef int32_t GGLfixed; GGLfixed gglFastDivx(GGLfixed n, GGLfixed d) { if ((d>>24) && ((d>>24)+1)) { n >>= 8; d >>= 8; } return gglMulx(n, gglRecip(d)); } 

Die antwort
Das Problem liegt im Ausdruck (d >> 24) + 1 .

Der Programmierer wollte überprüfen, ob die 8 höherwertigen Bits der Variablen d Einheiten enthalten, jedoch nicht alle Bits gleichzeitig. Mit anderen Worten, der Programmierer wollte überprüfen, ob sich ein anderer Wert als 0x00 und 0xFF im High-Byte befindet. Zuerst überprüfte er, ob die höchstwertigen Bits ungleich Null sind, indem er einen Ausdruck schrieb (d >> 24). Dann werden die hohen acht Bits auf das niedrige Byte verschoben. Gleichzeitig wird berechnet, dass das höchstwertige Vorzeichenbit in allen anderen Bits dupliziert wird. Das heißt, wenn die Variable d gleich 0b11111111'00000000'00000000'0000000000 ist, erhalten wir nach der Verschiebung den Wert 0b11111111'111111'111111111111111111111111. Durch Hinzufügen von 1 zum Wert 0xFFFFFFFF vom Typ int plant der Programmierer, 0 zu erhalten (-1 + 1 = 0). Somit prüft er mit dem Ausdruck ((d >> 24) +1), dass nicht alle hohen acht Bits gleich 1 sind.

Beim Verschieben wird das höchstwertige Bit jedoch nicht unbedingt "verschmiert". Der Standard sagt: „Der Wert von E1 >> E2 ist E1 rechtsverschobene E2-Bitpositionen. Wenn E1 einen vorzeichenlosen Typ hat oder wenn E1 einen vorzeichenbehafteten Typ und einen nicht negativen Wert hat, ist der Wert des Ergebnisses der integrale Bestandteil des Quotienten von E1 / 2 ^ E2. Wenn E1 einen vorzeichenbehafteten Typ und einen negativen Wert hat, ist der resultierende Wert implementierungsdefiniert . "

Dies ist also ein Beispiel für implementierungsdefiniertes Verhalten. Wie dieser Code funktioniert, hängt von der Mikroprozessorarchitektur und der Implementierung des Compilers ab. Nach der Verschiebung können in den höchstwertigen Bits durchaus Nullen auftreten, und dann ist das Ergebnis des Ausdrucks ((d >> 24) +1) immer anders als 0, dh es ist immer ein wahrer Wert.

In der Tat eine schwierige Aufgabe. Dieser Fehler wurde wie der vorherige im Artikel "Wir haben die Android-Quellcodes mit PVS-Studio überprüft, oder niemand ist perfekt ." Beschrieben.

2019


C ++


"GCC ist schuld"

 int foo(const unsigned char *s) { int r = 0; while(*s) { r += ((r * 20891 + *s *200) | *s ^ 4 | *s ^ 3) ^ (r >> 1); s++; } return r & 0x7fffffff; } 

Der Programmierer behauptet, dass dieser Code aufgrund des Fehlers des GCC 8-Compilers mit einem Fehler arbeitet.

Die antwort
Die Funktion liefert negative Werte. Der Grund dafür ist, dass der Compiler keinen Code für den bitweisen AND (&) -Operator generiert. Der Fehler ist auf undefiniertes Verhalten zurückzuführen. Der Compiler sieht, dass in der Variablen r ein bestimmter Betrag berücksichtigt wird. In diesem Fall werden nur positive Zahlen hinzugefügt. Überläufe der Variablen r sollten nicht auftreten, da dies sonst ein undefiniertes Verhalten ist, das der Compiler nicht berücksichtigen und in irgendeiner Weise berücksichtigen sollte. Da der Wert in der Variablen r nach dem Ende des Zyklus nicht negativ sein kann, ist die Operation r & 0x7fffffff zum Zurücksetzen des Vorzeichenbits überflüssig und der Compiler gibt einfach den Wert der Variablen r aus der Funktion zurück.

Fehler aus dem Artikel " PVS-Studio 6.26 Release ".

QT-Fehler

 static inline const QMetaObjectPrivate *priv(const uint* data) { return reinterpret_cast<const QMetaObjectPrivate*>(data); } bool QMetaEnum::isFlag() const { const int offset = priv(mobj->d.data)->revision >= 8 ? 2 : 1; return mobj && mobj->d.data[handle + offset] & EnumIsFlag; } 

Die antwort
Der Mobj- Zeiger ist unsicher. Zuerst wird dereferenziert und dann geprüft. Klassisch

Der Fehler wurde im Artikel " Dritter Qt 5 Test mit PVS-Studio " beschrieben.

C #


Infer.NET-Fehler

 public static void WriteAttribute(TextWriter writer, string name, object defaultValue, object value, Func<object, string> converter = null) { if ( defaultValue == null && value == null || value.Equals(defaultValue)) { return; } string stringValue = converter == null ? value.ToString() : converter(value); writer.Write($"{name}=\"{stringValue}\" "); } 

Die antwort
Im Ausdruck value.Equals (defaultValue) ist der Zugriff über den Wert null reference möglich. Dies geschieht mit solchen Variablenwerten, wenn defaultValue! = Null und value == null .

Der Fehler aus dem Artikel " Welche Fehler sind im Infer.NET-Code verborgen? "

FastReport-Fehler

 public class FastString { private const int initCapacity = 32; private void Init(int iniCapacity) { sb = new StringBuilder(iniCapacity); .... } public FastString() { Init(initCapacity); } public FastString(int iniCapacity) { Init(initCapacity); } public StringBuilder StringBuilder => sb; } .... Console.WriteLine(new FastString(256).StringBuilder.Capacity); 

Was wird auf der Konsole angezeigt? Was ist los mit der FastString- Klasse?

Die antwort
32 wird auf der Konsole angezeigt Der Grund ist ein Tippfehler im Variablennamen, der im Konstruktor an die Init- Methode übergeben wird:

 public FastString(int iniCapacity){ Init(initCapacity); } 

Der Konstruktorparameter iniCapacity wird nicht verwendet. Stattdessen wird die initCapacity- Konstante an die Init- Methode übergeben.

Der Fehler wurde im Artikel " Die schnellsten Meldungen im Wilden Westen. Und eine Handvoll Bugs dazu ... " beschrieben.

Roslyn Bug

 private SyntaxNode GetNode(SyntaxNode root) { var current = root; .... while (current.FullSpan.Contains(....)) { .... var nodeOrToken = current.ChildThatContainsPosition(....); .... current = nodeOrToken.AsNode(); } .... } public SyntaxNode AsNode() { if (_token != null) { return null; } return _nodeOrParent; } 

Die antwort
Der Zugriff ist über den Nullbezugsstrom im Ausdruck current.FullSpan.Contains (....) möglich . Die aktuelle Variable kann als Ergebnis der Ausführung der nodeOrToken.AsNode () -Methode einen Nullwert erhalten.

Fehler aus dem Artikel " Überprüfen des Quellcodes von Roslyn ".

Unity Bug

 .... staticFields = packedSnapshot.typeDescriptions .Where(t => t.staticFieldBytes != null & t.staticFieldBytes.Length > 0) .Select(t => UnpackStaticFields(t)) .ToArray() .... 

Die antwort
Tippfehler erlaubt: Anstelle des Operators && wurde der Operator & verwendet. Dies führt dazu, dass die Prüfung t.staticFieldBytes.Length> 0 immer durchgeführt wird, auch wenn die Nullvariable t.staticFieldBytes ist , was wiederum zu einem Zugriff über eine Nullreferenz führt.

Dieser Fehler wurde erstmals im Artikel " Analysieren von Fehlern in Open Unity3D-Komponenten " angezeigt .

Java


IntelliJ IDEA-Fehler

 private static boolean checkSentenceCapitalization(@NotNull String value) { List<String> words = StringUtil.split(value, " "); .... int capitalized = 1; .... return capitalized / words.size() < 0.2; // allow reasonable amount of // capitalized words } 

Es wird vorgeschlagen, festzustellen, warum die Anzahl der Wörter mit Großbuchstaben falsch berechnet wird.

Die antwort
Die Funktion sollte true zurückgeben, wenn weniger als 20% der Wörter mit einem Großbuchstaben beginnen. Die Prüfung funktioniert jedoch nicht, da eine Ganzzahldivision auftritt, deren Ergebnis nur die Werte 0 oder 1 sind. Die Funktion gibt nur dann einen falschen Wert zurück, wenn alle Wörter mit einem Großbuchstaben beginnen. In anderen Fällen erzeugt die Division 0 und die Funktion gibt true zurück.

Fehler aus dem Artikel " PVS-Studio für Java ".

Spotbugs Bug

 public static String getXMLType(@WillNotClose InputStream in) throws IOException { .... String s; int count = 0; while (count < 4) { s = r.readLine(); if (s == null) { break; } Matcher m = tag.matcher(s); if (m.find()) { return m.group(1); } } throw new IOException("Didn't find xml tag"); .... } 

Es wird vorgeschlagen, den XML-Tag-Suchfehler zu ermitteln.

Die antwort
Die Bedingung count <4 ist immer erfüllt, da die Anzahl der Variablen innerhalb der Schleife nicht erhöht wird. Es wurde davon ausgegangen, dass die Suche nach dem xml-Tag nur in den ersten vier Zeilen der Datei durchgeführt werden sollte. Aufgrund eines Fehlers wird jedoch die gesamte Datei gelesen.

Dieser Fehler wurde wie der vorherige im Artikel " PVS-Studio für Java " beschrieben.

Das ist alles Wir erwarten Sie bei den nächsten Konferenzen. Suchen Sie nach einem Einhornständer. Wir werden neue interessante Rätsel und natürlich Preise vergeben. Bis dann!



Wenn Sie diesen Artikel mit einem englischsprachigen Publikum teilen möchten, verwenden Sie bitte den Link zur Übersetzung: Sergey Khrenov. Lösungen für das Auffinden von Fehlern, die das PVS-Studio-Team auf Konferenzen in den Jahren 2018-2019 anbietet .

Source: https://habr.com/ru/post/de476272/


All Articles