
Moderne Computertechnologien, technische und Softwarelösungen - all dies erleichtert und beschleunigt die Umsetzung verschiedener wissenschaftlicher Forschungen erheblich. Oft ist die Computersimulation die einzige Möglichkeit, viele Theorien zu testen. Wissenschaftliche Software hat ihre eigenen Eigenschaften. Beispielsweise wird eine solche Software häufig sehr gründlichen Tests unterzogen, ist jedoch schlecht dokumentiert. Software wird jedoch von Menschen geschrieben, und Menschen machen Fehler. Fehler in wissenschaftlichen Programmen können die gesamte Forschung in Frage stellen. In diesem Artikel werden Dutzende von Problemen aufgelistet, die im Code des NCBI Genome Workbench-Softwarepakets enthalten sind.
Einführung
Die NCBI Genome Workbench bietet Forschern eine breite Palette von Werkzeugen zum Studieren und Analysieren genetischer Daten. Benutzer können Daten aus verschiedenen Quellen recherchieren und vergleichen, einschließlich der NCBI-Datenbanken (National Center for Biotechnology Information) oder ihrer eigenen persönlichen Daten.
Wie bereits erwähnt, wird wissenschaftliche Software in der Regel durch Unit-Tests gut abgedeckt. Bei der Überprüfung dieses Projekts wurden 85 Verzeichnisse mit Testdateien von der Analyse ausgeschlossen. Das sind ungefähr tausend Dateien. Möglicherweise liegt dies an den Anforderungen zum Testen verschiedener komplexer Algorithmen, die für verschiedene Studien erfunden wurden. Aber die Qualität des restlichen Codes (nicht des Tests) ist nicht so hoch, wie wir es gerne hätten. Wie in jedem Projekt, in dem sie sich noch nicht um die Einführung statischer Code-Analyse-Tools gekümmert haben :).
Daten zur Überprüfung (oder sogar Recherche) des Codes wurden vom statischen Code-Analysator für C / C ++ / C # / Java -
PVS-Studio bereitgestellt.
Nur zwei Zahlen, die Ihr Projekt ruinieren
Basierend auf unserer Fehlerdatenbank, die derzeit mehr als 12.000 ausgewählte Beispiele umfasst, bemerken und beschreiben wir spezifische Muster zum Schreiben von Code, die zu zahlreichen Fehlern führen. Zum Beispiel haben wir folgende Studien durchgeführt:
- Die Wirkung der letzten Zeile ;
- Die gefährlichste Funktion in der Welt von C / C ++ ;
- Logische Ausdrücke in C / C ++. Wie falsch sind die Profis ;
- Das Böse lebt in Vergleichsfunktionen .
Dieses Projekt war der Beginn der Beschreibung des neuen Musters. Wir sprechen über die Nummern
1 und
2 in den Namen von Variablen, zum Beispiel
Datei1 und
Datei2 usw. Es ist sehr leicht, zwei solche Variablen zu verwechseln. Dies ist ein Sonderfall von Tippfehlern im Code, aber ein solcher Fehler führt zu dem Wunsch, mit gleichnamigen Variablen zu arbeiten, die sich nur in den Nummern 1 und 2 am Ende des Namens unterscheiden.
Mit Blick auf die Zukunft werde ich sagen, dass alle oben aufgeführten Studien im Code dieses Projekts bestätigt wurden: D.
Betrachten Sie das erste Beispiel aus dem Genome Workbench-Projekt:
V501 Links und rechts vom '||' befinden sich identische Unterausdrücke '(! Loc1.IsInt () &&! Loc1.IsWhole ())'. Betreiber. nw_aligner.cpp 480
CRef<CSeq_align> CNWAligner::Run(CScope &scope, const CSeq_loc &loc1, const CSeq_loc &loc2, bool trim_end_gaps) { if ((!loc1.IsInt() && !loc1.IsWhole()) || (!loc1.IsInt() && !loc1.IsWhole())) { NCBI_THROW(CException, eUnknown, "Only whole and interval locations supported"); } .... }
Wir sehen zwei Variablen mit den Namen
loc1 und
loc2 . Und auch ein Fehler im Code: Die Variable
loc2 wird nicht verwendet, da stattdessen wieder
loc1 verwendet wird.
Ein weiteres Beispiel:
V560 Ein Teil des bedingten Ausdrucks ist immer falsch: s1.IsSet (). valid_biosource.cpp 3073
static bool s_PCRPrimerSetLess(const CPCRPrimerSet& s1, const CPCRPrimerSet& s2) { if (!s1.IsSet() && s1.IsSet()) { return true; } else if (s1.IsSet() && !s2.IsSet()) { return false; } else if (!s1.IsSet() && !s2.IsSet()) { return false; } else if (s1.Get().size() < s2.Get().size()) { return true; } else if (s1.Get().size() > s2.Get().size()) { return false; } else { ..... }
In der ersten Codezeile wurden die Variablen
s1 und
s2 verwechselt . Basierend auf dem Namen ist dies eine Vergleichsfunktion. Ein solcher Fehler kann jedoch überall auftreten, da der Programmierer durch die Benennung der Variablen
Nummer 1 und
Nummer 2 mit ziemlicher Sicherheit in Zukunft einen Fehler machen wird. Und je häufiger solche Namen in einer Funktion verwendet werden, desto höher ist die Wahrscheinlichkeit eines Fehlers.
Andere Tippfehler und Copy-Paste
V501 Links und rechts vom Operator '! ='
Gibt es identische Unterausdrücke: bd.bit_.bits [i]! = Bd.bit_.bits [i] bm.h 296
bool compare_state(const iterator_base& ib) const { .... if (this->block_type_ == 0 { if (bd.bit_.ptr != ib_db.bit_.ptr) return false; if (bd.bit_.idx != ib_db.bit_.idx) return false; if (bd.bit_.cnt != ib_db.bit_.cnt) return false; if (bd.bit_.pos != ib_db.bit_.pos) return false; for (unsigned i = 0; i < bd.bit_.cnt; ++i) { if (bd.bit_.bits[i] != bd.bit_.bits[i]) return false; } } .... }
Ich glaube, dass nach all den Überprüfungen die Größen der
Bit- Arrays der
Objekte bd.bit_ und
ib_db.bit_ gleich sind. Daher schrieb der Autor des Codes einen Zyklus für den elementweisen Vergleich des
Bit- Arrays, machte jedoch einen Tippfehler im Namen eines der verglichenen Objekte. Infolgedessen können verglichene Objekte in einigen Situationen fälschlicherweise als gleich angesehen werden.
Dieses Beispiel verdient den Artikel "Das
Böse lebt in Vergleichsfunktionen ".
V501 Links und rechts vom '||' befinden sich identische Unterausdrücke 'CFieldHandler :: QualifierNamesAreEquivalent (Feld, kFieldTypeSeqId)'. Betreiber. field_handler.cpp 152
bool CFieldHandlerFactory::s_IsSequenceIDField(const string& field) { if ( CFieldHandler::QualifierNamesAreEquivalent(field, kFieldTypeSeqId) || CFieldHandler::QualifierNamesAreEquivalent(field, kFieldTypeSeqId)) { return true; } else { return false; } }
Höchstwahrscheinlich ist eine der Prüfungen überflüssig. Ich habe in den
Codevariablen , die
kFieldTypeSeqId ähneln,
keine gefunden . Trotzdem ist aufgrund des Operators "||" ein zusätzlicher Funktionsaufruf möglich, der die Leistung beeinträchtigt.
Ein paar weitere Orte derselben Art mit einer Analysatorwarnung, die überprüft werden muss:
- V501 Links und rechts vom Operator '&&' befinden sich identische Unterausdrücke 'uf-> GetData (). IsBool ()'. Variation_utils.cpp 1711
- V501 Links und rechts vom Operator '&&' befinden sich identische Unterausdrücke 'uf-> GetData (). IsBool ()'. Variation_utils.cpp 1735
V766 Ein Element mit demselben Schlüssel 'kArgRemote' wurde bereits hinzugefügt. blast_args.cpp 3262
void CBlastAppArgs::x_IssueWarningsForIgnoredOptions(const CArgs& args) { set<string> can_override; .... can_override.insert(kArgOutputFormat); can_override.insert(kArgNumDescriptions); can_override.insert(kArgNumAlignments); can_override.insert(kArgMaxTargetSequences); can_override.insert(kArgRemote);
Der Analysator hat festgestellt, dass dem
eingestellten Behälter 2 identische Werte hinzugefügt wurden. Denken Sie daran, dass dieser Container nur eindeutige Werte speichert, sodass ihm keine Duplikate hinzugefügt werden.
Code wie der obige wird häufig mit der Copy-Paste-Methode geschrieben. Möglicherweise gibt es einfach einen zusätzlichen Wert, oder der Autor hat beim Kopieren vergessen, eine der Variablen umzubenennen. Wenn Sie einen zusätzlichen Aufruf zum
Einfügen entfernen
, wird der Code leicht optimiert, was jedoch nicht von Bedeutung ist. Noch wichtiger ist, dass hier möglicherweise ein schwerwiegender Fehler aufgrund eines fehlenden Elements in der Gruppe ausgeblendet wird.
V523 Die Anweisung 'then' entspricht dem nachfolgenden Codefragment. vcf_reader.cpp 1105
bool CVcfReader::xAssignFeatureLocationSet(....) { .... if (data.m_SetType == CVcfData::ST_ALL_DEL) { if (data.m_strRef.size() == 1) {
Die Funktion enthält große und vollständig identische Codefragmente. Sie enthalten jedoch verschiedene begleitende Kommentare. Der Code ist nicht optimal, verwirrend geschrieben und enthält möglicherweise einen Fehler.
Die gesamte Liste der verdächtigen Orte mit der if-else-Anweisung sieht folgendermaßen aus:
- V523 Die Anweisung 'then' entspricht der Anweisung 'else'. blk.c 2142
- V523 Die Anweisung 'then' entspricht dem nachfolgenden Codefragment. odbc.c 379
- V523 Die Anweisung 'then' entspricht dem nachfolgenden Codefragment. odbc.c 1414
- V523 Die Anweisung 'then' entspricht der Anweisung 'else'. seqdbvol.cpp 1922
- V523 Die Anweisung 'then' entspricht der Anweisung 'else'. seqdb_demo.cpp 466
- V523 Die Anweisung 'then' entspricht dem nachfolgenden Codefragment. blast_engine.c 1917
- V523 Die Anweisung 'then' entspricht der Anweisung 'else'. blast_filter.c 420
- V523 Die Anweisung 'then' entspricht der Anweisung 'else'. blast_parameters.c 636
- V523 Die Anweisung 'then' entspricht der Anweisung 'else'. unordered_spliter.cpp 684
- V523 Die Anweisung 'then' entspricht der Anweisung 'else'. bme.cpp 333
- V523 Die Anweisung 'then' entspricht der Anweisung 'else'. gme.cpp 484
/ * mit Sicherheit ist am besten pedantisch * /
V597 Der Compiler könnte den Funktionsaufruf 'memset' löschen, mit dem der Puffer 'passwd_buf' geleert wird. Die Funktion memset_s () sollte verwendet werden, um die privaten Daten zu löschen. Herausforderung.c 366
void tds_answer_challenge(....) { #define MAX_PW_SZ 14 .... if (ntlm_v == 1) { .... memset(hash, 0, sizeof(hash)); memset(passwd_buf, 0, sizeof(passwd_buf)); memset(ntlm2_challenge, 0, sizeof(ntlm2_challenge)); } else { .... } }
Wie Sie wahrscheinlich bereits vermutet haben, wurde im Abschnittstitel ein lustiger Kommentar zur Sicherheit durch Code verwendet.
Kurz gesagt, die
Memset- Funktion wird vom Compiler entfernt, da geleerte Puffer nicht mehr verwendet werden. Und Daten wie
Hash oder
passwd_buf sind eigentlich keine Nullen. Weitere Informationen zu diesem nicht offensichtlichen Compiler-Mechanismus finden Sie im Artikel "
Sicheres Löschen privater Daten ".
V597 Der Compiler könnte den Funktionsaufruf 'memset' löschen, mit dem das Objekt 'answer' gelöscht wird. Die Funktion memset_s () sollte verwendet werden, um die privaten Daten zu löschen. Herausforderung.c 561
static TDSRET tds7_send_auth(....) { .... memset(&answer, 0, sizeof(TDSANSWER)); return tds_flush_packet(tds); }
Dies war nicht das einzige Beispiel mit Kommentaren zu „Sicherheit“. Den Kommentaren nach zu urteilen, kann davon ausgegangen werden, dass Sicherheit für das Projekt wirklich wichtig ist. Daher füge ich die gesamte nicht kleine Liste der identifizierten Probleme bei:
- V597 Der Compiler könnte den Funktionsaufruf 'memset' löschen, mit dem das 'heap'-Objekt geleert wird. Die Funktion memset_s () sollte verwendet werden, um die privaten Daten zu löschen. ncbi_heapmgr.c 1300
- V597 Der Compiler könnte den Funktionsaufruf 'memset' löschen, mit dem das 'context'-Objekt geleert wird. Die Funktion memset_s () sollte verwendet werden, um die privaten Daten zu löschen. Herausforderung.c 167
- V597 Der Compiler könnte den Funktionsaufruf 'memset' löschen, mit dem das Objekt 'ks' gelöscht wird. Die Funktion memset_s () sollte verwendet werden, um die privaten Daten zu löschen. Herausforderung.c 339
- V597 Der Compiler könnte den Funktionsaufruf 'memset' löschen, mit dem das Objekt 'md5_ctx' geleert wird. Die Funktion memset_s () sollte verwendet werden, um die privaten Daten zu löschen. Herausforderung.c 353
- V597 Der Compiler könnte den Funktionsaufruf 'memset' löschen, mit dem der 'Hash'-Puffer geleert wird. Die Funktion memset_s () sollte verwendet werden, um die privaten Daten zu löschen. Challenge.c 365
- V597 Der Compiler könnte den Funktionsaufruf 'memset' löschen, mit dem das Objekt 'ks' gelöscht wird. Die Funktion memset_s () sollte verwendet werden, um die privaten Daten zu löschen. Herausforderung.c 406
- V597 Der Compiler könnte den Funktionsaufruf 'memset' löschen, mit dem das Objekt 'ntlm_v2_response' gelöscht wird. Die Funktion memset_s () sollte verwendet werden, um die privaten Daten zu löschen. login.c 795
- V597 Der Compiler könnte den Funktionsaufruf 'memset' löschen, mit dem das Objekt 'answer' gelöscht wird. Die Funktion memset_s () sollte verwendet werden, um die privaten Daten zu löschen. login.c 801
- V597 Der Compiler könnte den Funktionsaufruf 'memset' löschen, mit dem der 'Paket'-Puffer geleert wird. Die Funktion memset_s () sollte verwendet werden, um die privaten Daten zu löschen. numeric.c 256
- V597 Der Compiler könnte den Funktionsaufruf 'memset' löschen, mit dem der 'Paket'-Puffer geleert wird. Die Funktion memset_s () sollte verwendet werden, um die privaten Daten zu löschen. numeric.c 110
- V597 Der Compiler könnte den Funktionsaufruf 'memset' löschen, mit dem der 'pwd'-Puffer geleert wird. Die Funktion memset_s () sollte verwendet werden, um die privaten Daten zu löschen. getpassarg.c 50
- V597 Der Compiler könnte den Funktionsaufruf 'memset' löschen, mit dem das 'context'-Objekt geleert wird. Die Funktion memset_s () sollte verwendet werden, um die privaten Daten zu löschen. Herausforderung.c 188
- V597 Der Compiler könnte den Funktionsaufruf 'memset' löschen, mit dem der 'buf'-Puffer geleert wird. Die Funktion memset_s () sollte verwendet werden, um die privaten Daten zu löschen. Herausforderung.c 243
- V597 Der Compiler könnte den Funktionsaufruf 'memset' löschen, mit dem der Puffer 'ntlm_v2_hash' geleert wird. Die Funktion memset_s () sollte verwendet werden, um die privaten Daten zu löschen. Herausforderung.c 309
- V597 Der Compiler könnte den Funktionsaufruf 'memset' löschen, mit dem das Objekt 'md5_ctx' geleert wird. Die Funktion memset_s () sollte verwendet werden, um die privaten Daten zu löschen. Herausforderung.c 354
- V597 Der Compiler könnte den Funktionsaufruf 'memset' löschen, mit dem der Puffer 'passwd_buf' geleert wird. Die Funktion memset_s () sollte verwendet werden, um die privaten Daten zu löschen. Herausforderung.c 380
- V597 Der Compiler könnte den Funktionsaufruf 'memset' löschen, mit dem das Objekt 'ks' gelöscht wird. Die Funktion memset_s () sollte verwendet werden, um die privaten Daten zu löschen. Herausforderung.c 393
- V597 Der Compiler könnte den Funktionsaufruf 'memset' löschen, mit dem der 'Hash'-Puffer geleert wird. Die Funktion memset_s () sollte verwendet werden, um die privaten Daten zu löschen. Herausforderung.c 394
- V597 Der Compiler könnte den Funktionsaufruf 'memset' löschen, mit dem der Puffer 'ntlm2_challenge' geleert wird. Die Funktion memset_s () sollte verwendet werden, um die privaten Daten zu löschen. Herausforderung.c 395
- V597 Der Compiler könnte den Funktionsaufruf 'memset' löschen, mit dem das Objekt 'ks' gelöscht wird. Die Funktion memset_s () sollte verwendet werden, um die privaten Daten zu löschen. Herausforderung.c 419
- V597 Der Compiler könnte den Funktionsaufruf 'memset' löschen, mit dem das Objekt 'ntlm_v2_response' gelöscht wird. Die Funktion memset_s () sollte verwendet werden, um die privaten Daten zu löschen. Herausforderung.c 556
Verdächtige Zyklen
V534 Es ist wahrscheinlich, dass eine falsche Variable innerhalb des '
for' -Operators verglichen wird. Betrachten Sie die Überprüfung von 'i'. taxFormat.cpp 569
void CTaxFormat::x_LoadTaxTree(void) { .... for(size_t i = 0; i < alignTaxids.size(); i++) { int tax_id = alignTaxids[i]; .... for(size_t j = 0; i < taxInfo.seqInfoList.size(); j++) { SSeqInfo* seqInfo = taxInfo.seqInfoList[j]; seqInfo->taxid = newTaxid; } .... } .... }
Ich denke, im Zustand der inneren Schleife habe ich die Variable zufällig erhalten. Stattdessen sollte die Variable
j verwendet werden.
V535 Die Variable 'i' wird für diese Schleife und für die äußere Schleife verwendet. Überprüfen Sie die Zeilen: 302, 309. sls_alp.cpp 309
alp::~alp() { .... if(d_alp_states) { for(i=0;i<=d_nalp;i++)
Zwei verschachtelte identische Zyklen, in denen auch der globale Zähler zurückgesetzt wird, sehen sehr verdächtig aus. Entwickler sollten überprüfen, was hier passiert.
Array-Indizierung abnormal
V520 Der
Kommaoperator ',' im Array-
Indexausdruck '[- i2, - k]'. nw_spliced_aligner16.cpp 564
void CSplicedAligner16::x_DoBackTrace ( const Uint2* backtrace_matrix, CNWAligner::SAlignInOut* data, int i_global_max, int j_global_max) { .... while(intron_length < m_IntronMinSize || (Key & donor) == 0) { Key = backtrace_matrix[--i2, --k]; ++intron_length; data->m_transcript.push_back(eTS_Intron); } .... }
Ich muss sofort sagen, dass es keinen Fehler zu geben scheint (vorerst lol). Betrachten Sie die folgende Zeile:
Key = backtrace_matrix[--i2, --k];
Das Wort "Matrix" und die doppelte Indizierung können darauf hinweisen, dass das Array zweidimensional ist, dies ist jedoch nicht der Fall. Dies ist ein regulärer Zeiger auf ein Array von Ganzzahlen. Die
V520- Diagnose wurde jedoch nicht nur
angezeigt . Programmierer sind wirklich verwirrt darüber, wie zweidimensionale Arrays indiziert werden sollen.
In diesem Fall hat der Autor einfach beschlossen, in einer Codezeile zu speichern, obwohl er folgendermaßen schreiben könnte:
--i2; Key = backtrace_matrix[--k];
V661 Ein verdächtiger Ausdruck 'A [B == C]'. Bedeutete wahrscheinlich 'A [B] == C'. ncbi_service_connector.c 180
static EHTTP_HeaderParse s_ParseHeader(const char* header, ....) { .... if (sscanf(header, "%u.%u.%u.%u%n", &i1, &i2, &i3, &i4, &n) < 4 || sscanf(header + n, "%hu%x%n", &uuu->port, &tkt, &m) < 2 || (header[m += n] && !(header[m] == '$') && !isspace((unsigned char)((header + m) [header[m] == '$'])))) { break; } .... }
Ein weiteres Beispiel für Code, in dem ich lange versucht habe zu verstehen, was vor sich geht: D. Die Funktion
isspace () überprüft das Zeichen mit dem Index
m . Wenn dieses Zeichen jedoch '$' ist, wird das Zeichen mit dem Index
m + 1 an die Funktion übergeben. Darüber hinaus war der Vergleich mit '$' bereits im Voraus. Vielleicht liegt hier kein Fehler vor, aber der Code kann definitiv klarer umgeschrieben werden.
V557 Array-Überlauf ist möglich. Der 'Zeilen'-Index zeigt über die Array-Grenze hinaus. aln_reader.cpp 412
bool CAlnReader::x_IsGap(TNumrow row, TSeqPos pos, const string& residue) { if (m_MiddleSections.size() == 0) { x_CalculateMiddleSections(); } if (row > m_MiddleSections.size()) { return false; } if (pos < m_MiddleSections[row].first) { .... } .... }
Hier liegt ein schwerwiegender Fehler vor. Die korrekte Überprüfung des Zeilenindex sollte folgendermaßen aussehen:
if (row >= m_MiddleSections.size()) { return false; }
Andernfalls ist es möglich, auf Daten außerhalb des
MiddleSections- Vektors
zuzugreifen .
Viele weitere solcher Orte:
- V557 Array-Überlauf ist möglich. Der 'i'-Index zeigt über die Array-Grenze hinaus. resource_pool.hpp 388
- V557 Array-Überlauf ist möglich. Der 'Zeilen'-Index zeigt über die Array-Grenze hinaus. aln_reader.cpp 418
- V557 Array-Überlauf ist möglich. Der Index 'fmt_idx' zeigt über die Array-Grenze hinaus. seq_writer.cpp 384
- V557 Array-Überlauf ist möglich. Der Index 'fmt_idx' zeigt über die Array-Grenze hinaus. blastdb_formatter.cpp 183
- V557 Array-Überlauf ist möglich. Der 'num'-Index zeigt über die Array-Grenze hinaus. newcleanupp.cpp 13035
Wie man Misstrauen gegenüber Funktionen verdient
V570 Die Variable 'm_onClickFunction' wird sich selbst zugewiesen. alngraphic.hpp 103
void SetOnClickFunctionName(string onClickFunction) { m_onClickFunction = m_onClickFunction; }
Es gibt nichts zu kommentieren. Sie können nur mit der Person sympathisieren, die auf etwas geklickt, geklickt, aber nichts geändert hat.
Zwei weitere Fälle, in denen mir Variablen zugewiesen werden, führen zu einer Liste:
- V570 Die Variable 'iter-> level' wird sich selbst zugewiesen. align_format_util.cpp 189
- V570 Die Variable 'd_elements_values [ind]' wird sich selbst zugewiesen. sls_alp_data.cpp 1416
V763 Der Parameter 'w1' wird vor seiner Verwendung immer in den Funktionskörper umgeschrieben. bmfunc.h 5363
Eine Funktion, bei der das Argument unmittelbar nach Eingabe der Funktion ausgefranst ist, kann für die Entwickler, die sie verwenden, irreführend sein. Der Code sollte doppelt überprüft werden.
Klassendesignfehler
V688 Das Funktionsargument 'm_qsrc' hat denselben Namen wie eines der Klassenmitglieder, was zu Verwirrung führen kann. compart_matching.cpp 873
class CElementaryMatching: public CObject { .... ISequenceSource * m_qsrc; .... void x_CreateIndex (ISequenceSource *m_qsrc, EIndexMode index_more, ....); void x_CreateRemapData(ISequenceSource *m_qsrc, EIndexMode mode); void x_LoadRemapData (ISequenceSource *m_qsrc, const string& sdb); .... };
Sofort enthalten 3 Klassenfunktionen Argumente, deren Namen mit dem Klassenfeld übereinstimmen. Dies kann zu Fehlern in Funktionskörpern führen: Der Programmierer glaubt möglicherweise, mit einem Mitglied der Klasse zu arbeiten, und ändert tatsächlich den Wert der lokalen Variablen.
V614 Nicht initialisierte Variable 'm_BitSet' verwendet. SnpBitAttributes.hpp 187
Einer der Konstruktoren arbeitet
schlampig mit der Variablen
m_BitSet . Tatsache ist, dass die Variable nicht initialisiert ist. Sein "Garbage" -Wert wird bei der ersten Iteration der Schleife verwendet, wonach die Initialisierung erfolgt. Dies ist ein sehr schwerwiegender Fehler, der zu undefiniertem Programmverhalten führt.
V603 Das Objekt wurde erstellt, wird jedoch nicht verwendet. Wenn Sie den Konstruktor aufrufen möchten, sollte 'this-> SIntervalComparisonResult :: SIntervalComparisonResult (....)' verwendet werden. compare_feats.hpp 100
Vor sehr langer Zeit bin ich bei der Überprüfung von Projekten nicht auf solche Fehler gestoßen. Das Problem ist aber immer noch relevant. Der Fehler besteht darin, dass beim Aufrufen des parametrisierten Konstruktors auf diese Weise ein temporäres Objekt erstellt und entfernt wird. Und die Klassenfelder bleiben nicht initialisiert. Ein anderer Konstruktor sollte über die Initialisierungsliste aufgerufen werden (siehe
Delegieren des Konstruktors ).
V591 Non-void-Funktion sollte einen Wert zurückgeben. bio_tree.hpp 266
Der Analysator geht davon aus, dass die Zeile in der überladenen Anweisung fehlt:
return *this;
V670 Das nicht initialisierte Klassenmitglied 'm_OutBlobIdOrData' wird zum Initialisieren des 'm_StdOut'-Mitglieds verwendet. Denken Sie daran, dass Mitglieder in der Reihenfolge ihrer Deklarationen innerhalb einer Klasse initialisiert werden. remote_app.hpp 215
class NCBI_XCONNECT_EXPORT CRemoteAppResult { public: CRemoteAppResult(CNetCacheAPI::TInstance netcache_api, size_t max_inline_size = kMaxBlobInlineSize) : m_NetCacheAPI(netcache_api), m_RetCode(-1), m_StdOut(netcache_api, m_OutBlobIdOrData, m_OutBlobSize), m_OutBlobSize(0), m_StdErr(netcache_api, m_ErrBlobIdOrData, m_ErrBlobSize), m_ErrBlobSize(0), m_StorageType(eBlobStorage), m_MaxInlineSize(max_inline_size) { } .... };
3 Analysatorwarnungen werden sofort an dieses Codefragment ausgegeben. Klassenfelder werden nicht in der Reihenfolge initialisiert, in der sie in der Initialisierungsliste aufgeführt sind, sondern so, wie sie in der Klasse deklariert sind. Die klassische Fehlerursache ist, dass sich nicht alle Programmierer an diese Regel erinnern oder sie kennen. Hier und in der Initialisierungsliste ist nur die falsche Reihenfolge. Man hat das Gefühl, dass die Liste der Felder in zufälliger Reihenfolge eingegeben wurde.
V746 Objektschneiden. Eine Ausnahme sollte eher durch Referenz als durch Wert abgefangen werden. cobalt.cpp 247
void CMultiAligner::SetQueries(const vector< CRef<objects::CBioseq> >& queries) { .... try { seq_loc->SetId(*it->GetSeqId()); } catch (objects::CObjMgrException e) { NCBI_THROW(CMultiAlignerException, eInvalidInput, (string)"Missing seq-id in bioseq. " + e.GetMsg()); } m_tQueries.push_back(seq_loc); .... }
Das Abfangen von Ausnahmen nach Wert kann zum Verlust einiger Informationen über die Ausnahme führen, da ein neues Objekt erstellt wird. Es ist viel besser und sicherer, eine Ausnahme als Referenz zu erfassen.
Ähnliche Orte:
- V746 Objektschneiden. Eine Ausnahme sollte eher durch Referenz als durch Wert abgefangen werden. agp_validate_reader.cpp 562
- V746 Objektschneiden. Eine Ausnahme sollte eher durch Referenz als durch Wert abgefangen werden. aln_build_app.cpp 320
- V746 Objektschneiden. Eine Ausnahme sollte eher durch Referenz als durch Wert abgefangen werden. aln_test_app.cpp 458
- V746 Objektschneiden. Eine Ausnahme sollte eher durch Referenz als durch Wert abgefangen werden. cobalt.cpp 691
- V746 Objektschneiden. Eine Ausnahme sollte eher durch Referenz als durch Wert abgefangen werden. cobalt.cpp 719
- V746 Objektschneiden. Eine Ausnahme sollte eher durch Referenz als durch Wert abgefangen werden. cobalt.cpp 728
- V746 Objektschneiden. Eine Ausnahme sollte eher durch Referenz als durch Wert abgefangen werden. cobalt.cpp 732
Informationen zu nicht erreichbarem Code und anderen Problemen bei der Codeausführung
V779 Nicht erreichbarer Code erkannt. Möglicherweise liegt ein Fehler vor. merge_tree_core.cpp 627
bool CMergeTree::x_FindBefores_Up_Iter(....) { .... FirstFrame->Curr = StartCurr; FirstFrame->Returned = false; FirstFrame->VisitCount = 0; FrameStack.push_back(FirstFrame); while(!FrameStack.empty()) { .... if(Rel == CEquivRange::eAfter) { Frame->Returned = false; FrameStack.pop_back(); continue; } else if(Rel == CEquivRange::eBefore) { .... continue; } else { if(Frame->VisitCount == 0) { .... continue; } else { .... continue; } } Frame->Returned = false;
Der Code der bedingten Anweisung ist so geschrieben, dass absolut alle Zweige des Codes mit der
continue- Anweisung enden. Dies führte dazu, dass sich in der
while-Schleife mehrere Zeilen nicht erreichbaren Codes bildeten. Diese Zeilen sehen sehr verdächtig aus. Dieses Problem trat höchstwahrscheinlich nach der Überarbeitung des Codes auf und erfordert nun eine sorgfältige Codeüberprüfung.
V519 Der Variablen 'interval_width' werden zweimal nacheinander Werte zugewiesen. Vielleicht ist das ein Fehler. Überprüfen Sie die Zeilen: 454, 456. aln_writer.cpp 456
void CAlnWriter::AddGaps(....) { .... switch(exon_chunk->Which()) { case CSpliced_exon_chunk::e_Match: interval_width = exon_chunk->GetMatch(); case CSpliced_exon_chunk::e_Mismatch: interval_width = exon_chunk->GetMismatch(); case CSpliced_exon_chunk::e_Diag: interval_width = exon_chunk->GetDiag(); genomic_string.append(....); product_string.append(....); genomic_pos += interval_width; product_pos += interval_width/res_width; break; .... } .... }
Die Variable
interval_width wird mehrmals überschrieben, weil In Fallzweigen gibt es keine
break- Anweisungen. Obwohl ein Klassiker, aber ein sehr schlimmer Fehler.
Noch ein paar verdächtige Orte:
- V779 Nicht erreichbarer Code erkannt. Möglicherweise liegt ein Fehler vor. dbapi_driver_utils.cpp 351
- V779 Nicht erreichbarer Code erkannt. Möglicherweise liegt ein Fehler vor. net.c 780
- V779 Nicht erreichbarer Code erkannt. Möglicherweise liegt ein Fehler vor. bcp.c 1495
- V779 Nicht erreichbarer Code erkannt. Möglicherweise liegt ein Fehler vor. remote_blast.cpp 1470
- V779 Nicht erreichbarer Code erkannt. Möglicherweise liegt ein Fehler vor. remote_blast.cpp 1522
V571 Wiederkehrende Prüfung. Die Bedingung 'if (m_QueryOpts-> filter_options)' wurde bereits in Zeile 703 überprüft. Blast_options_local_priv.hpp 713
inline void CBlastOptionsLocal::SetFilterString(const char* f) { .... if (m_QueryOpts->filtering_options)
Offensichtlich muss der Zweig
else neu geschrieben werden. Ich habe einige Ideen, die ich mit dem Zeiger
m_QueryOpts-> filtering_options machen wollte , aber der Code ist immer noch irgendwie verwirrend. Ich appelliere an die Autoren des Kodex.
Nun, das Problem kommt nicht alleine:
- V571 Wiederkehrende Prüfung. Die Bedingung 'if (Schlafzeit)' wurde bereits in Zeile 205 überprüft. Request_control.cpp 208
- V571 Wiederkehrende Prüfung. Die Bedingung 'if (assignValue.empty ())' wurde bereits in Zeile 712 überprüft. Classstr.cpp 718
Datenlesefehler
V739 EOF sollte nicht mit einem Wert vom Typ 'char' verglichen werden. Der 'linestring [0]' sollte vom Typ 'int' sein. alnread.c 3509
static EBool s_AfrpInitLineData( .... char* linestring = readfunc (pfile); .... while (linestring != NULL && linestring [0] != EOF) { s_TrimSpace (&linestring); .... } .... }
Zeichen, die Sie mit EOF vergleichen möchten, sollten nicht in
Zeichenvariablen gespeichert werden. Andernfalls besteht die Gefahr, dass aus einem Zeichen mit dem Wert 0xFF (255) -1 wird und wie am Ende einer Datei (EOF) interpretiert wird. Auch (nur für den Fall) lohnt es sich, die Implementierung der
Readfunc- Funktion zu überprüfen.
V663 Endlosschleife ist möglich. Die Bedingung 'cin.eof ()' reicht nicht aus, um die Schleife zu verlassen.
Fügen Sie dem bedingten Ausdruck möglicherweise den Funktionsaufruf 'cin.fail ()' hinzu. ncbicgi.cpp 1564 typedef std::istream CNcbiIstream; void CCgiRequest::Serialize(CNcbiOstream& os) const { .... CNcbiIstream* istrm = GetInputStream(); if (istrm) { char buf[1024]; while(!istrm->eof()) { istrm->read(buf, sizeof(buf)); os.write(buf, istrm->gcount()); } } }
Der Analysator hat einen möglichen Fehler festgestellt, aufgrund dessen eine Endlosschleife auftreten kann. Wenn beim Lesen von Daten ein Fehler auftritt, gibt der Aufruf der Funktion eof () immer false zurück . Um die Schleife in diesem Fall abzuschließen, ist eine zusätzliche Überprüfung des von der Funktion fail () zurückgegebenen Werts erforderlich .Verschiedene Fehler
V502 Vielleicht arbeitet der Operator '?:' Anders als erwartet. Der Operator '?:' Hat eine niedrigere Priorität als der Operator '&&'. ncbi_connutil.c 1135 static const char* x_ClientAddress(const char* client_host, int local_host) { .... if ((client_host == c && x_IsSufficientAddress(client_host)) || !(ip = *c && !local_host ? SOCK_gethostbyname(c) : SOCK_GetLocalHostAddress(eDefault)) || SOCK_ntoa(ip, addr, sizeof(addr)) != 0 || !(s = (char*) malloc(strlen(client_host) + strlen(addr) + 3))) { return client_host; } .... }
Achten Sie auf den Ausdruck: !local_host ? SOCK_gethostbyname(c) : SOCK_GetLocalHostAddress(eDefault)
Es wird nicht wie vom Programmierer erwartet berechnet, da der gesamte Ausdruck folgendermaßen aussieht: ip = *c && !local_host ? SOCK_gethostbyname(c) : SOCK_GetLocalHostAddress(...)
Die && Operatorpriorität ist höher als ?: . Aus diesem Grund wird der Code nicht wie beabsichtigt ausgeführt.V561 Es ist wahrscheinlich besser, der Variablen 'seq' einen Wert zuzuweisen, als ihn erneut zu deklarieren. Vorherige Deklaration: validator.cpp, Zeile 490. validator.cpp 492 bool CValidator::IsSeqLocCorrectlyOrdered(const CSeq_loc& loc, CScope& scope) { CBioseq_Handle seq; try { CBioseq_Handle seq = scope.GetBioseqHandle(loc); } catch (CObjMgrException& ) {
Da der Programmierer eine neue seq- Variable im try / catch-Abschnitt deklariert , bleibt die andere seq- Variable nicht initialisiert und wird unten im Code verwendet.V562 Es ist seltsam, einen Wert vom Typ Bool mit einem Wert von 0 zu vergleichen: (((Status) & 0x7f) == 0)! = 0. ncbi_process.cpp 111 bool CProcess::CExitInfo::IsExited(void) const { EXIT_INFO_CHECK; if (state != eExitInfo_Terminated) { return false; } #if defined(NCBI_OS_UNIX) return WIFEXITED(status) != 0; #elif defined(NCBI_OS_MSWIN)
Nichts war schlecht, aber WIFEXITED stellte sich auf diese Weise als Makroöffnung heraus: return (((status) & 0x7f) == 0) != 0;
Es stellt sich heraus, dass die Funktion den entgegengesetzten Wert zurückgibt.Im Code gab es eine andere solche Funktion, die eine Warnung ausgab:- V562 Es ist seltsam, einen Wert vom Typ Bool mit einem Wert von 0 zu vergleichen. Ncbi_process.cpp 126
V595 Der Zeiger 'dst_len' wurde verwendet, bevor er gegen nullptr verifiziert wurde. Überprüfen Sie die Zeilen: 309, 315. zlib.cpp 309 bool CZipCompression::CompressBuffer( const void* src_buf, size_t src_len, void* dst_buf, size_t dst_size, size_t* dst_len) { *dst_len = 0;
Der Zeiger dst_len wird ganz am Anfang der Funktion dereferenziert, während der Code weiter auf Gleichheit mit Null überprüft wird. Im Code ist ein Fehler aufgetreten , der zu undefiniertem Verhalten führt, wenn der Zeiger dst_len nullptr ist .V590 Überprüfen Sie den Ausdruck 'ch! =' \ 0 '&& ch ==' ''. Der Ausdruck ist übertrieben oder enthält einen Druckfehler. cleanup_utils.cpp 580 bool Asn2gnbkCompressSpaces(string& val) { .... while (ch != '\0' && ch == ' ') { ptr++; ch = *ptr; } .... }
Die Bedingung zum Stoppen der Schleife hängt nur davon ab, ob das Zeichen ch ein Leerzeichen ist oder nicht. Der Ausdruck kann wie folgt vereinfacht werden: while (ch == ' ') { .... }
Fazit
Die Verwendung von Computerprogrammen in der wissenschaftlichen Forschung hilft und wird helfen, Entdeckungen zu machen. Hoffen wir, dass besonders wichtige nicht durch Tippfehler übersehen werden.Ich lade die Entwickler des NCBI Genome Workbench-Projekts ein, sich mit uns in Verbindung zu setzen, und wir werden einen vollständigen Bericht des PVS-Studio-Analysators bereitstellen.Ich hoffe, diese kleine Code-Recherche hilft, viele Fehler zu beheben und die Zuverlässigkeit des Projekts im Allgemeinen zu verbessern. Versuchen Sie, PVS-Studio mit dem Code Ihrer Projekte auszuführen, falls Sie dies noch nicht getan haben. Es könnte dir gefallen :).
Wenn Sie diesen Artikel einem englischsprachigen Publikum zugänglich machen möchten, verwenden Sie bitte den Link zur Übersetzung: Svyatoslav Razmyslov. NCBI Genome Workbench: Wissenschaftliche Forschung unter Bedrohung