Testen eines OpenJDK-Projekts mit PVS-Studio

    Mitautor: Roman Fomichev.

    Derzeit öffnen viele Projekte ihren Quellcode und ermöglichen der Community interessierter Entwickler, Änderungen daran vorzunehmen. Wir werden eines dieser Projekte - OpenJDK - überprüfen und Entwicklern helfen, ihren Code zu verbessern.

    Einleitung


    OpenJDK (Open Java Development Kit) - ein Projekt zum Erstellen einer Implementierung der Java-Plattform (Java SE), die ausschließlich aus kostenlosem und Open Source-Code besteht. Das Projekt wurde 2006 von Sun ins Leben gerufen. Das Projekt verwendet mehrere Sprachen - C, C ++ und Java. Wir interessieren uns für Quellcodes, die in C und C ++ geschrieben sind. Verwenden Sie zur Überprüfung die 9. Version von OpenJDK. Der Code für diese Java-Plattform-Implementierung ist im Mercurial-Repository verfügbar .

    Das Projekt wurde mit dem Static Code Analyzer von PVS-Studio verifiziert. Es implementiert viele Diagnoseregeln, mit denen Sie eine große Anzahl von Fehlern finden können, die während der Programmierung gemacht wurden, einschließlich derer, die mit einem einfachen Code-Scan schwer zu erkennen sind. Einige dieser Fehler wirken sich nicht auf die Logik des Programms aus, andere können traurige Folgen für die Programmausführung haben. Die Analyseseite enthält Beispiele für Fehler, die in anderen Projekten gefunden wurden. Für die Analyse stehen Projekte in den Sprachen C, C ++ und C # zur Verfügung. Analyzer Download eine Testversion finden Sie unter dem Link .

    Fehler in logischen Ausdrücken




    Betrachten Sie zunächst die Fehler in logischen Ausdrücken:
    int StubAssembler::call_RT(....) {
    #ifdef _LP64
      // if there is any conflict use the stack
      if (arg1 == c_rarg2 || arg1 == c_rarg3 ||
          arg2 == c_rarg1 || arg1 == c_rarg3 ||
          arg3 == c_rarg1 || arg1 == c_rarg2) {
      ....
    }

    PVS-Studio Warnung: V501 Es gibt identische Unterausdrücke 'arg1 == c_rarg3' links und rechts vom '||' Betreiber. c1_Runtime1_x86.cpp 174

    Der Analysator meldet eine Verdoppelung der Prüfung arg1 == c_rarg3. In diesem Fragment liegt entweder eine redundante Prüfung oder, noch schlimmer, ein logischer Fehler vor. Möglicherweise hätte anstelle einer doppelten Bedingung etwas anderes überprüft werden müssen. Für Entwickler ist es sinnvoll, sich diesen Code genauer anzusehen.

    Unter der gleichen Bedingung gibt es einen weiteren sich wiederholenden Ausdruck arg1 == c_rarg2 :

    Warnung PVS-Studio: V501 Links und rechts vom '||' stehen identische Unterausdrücke 'arg1 == c_rarg2'. Betreiber. c1_Runtime1_x86.cpp 174

    Diese Warnungen zeigen besonders gut, dass ein statischer Analysator erforderlich ist. Bei einer großen Anzahl von Ausdrücken des gleichen Typs ist es sehr einfach, einen Fehler zu machen, der bei einer oberflächlichen Überprüfung des Codes nur schwer zu erkennen ist.

    Das folgende Snippet hat eine "unvollständige" Prüfung unter der Bedingung der Ideal- Methode erhalten :
    Node *AddLNode::Ideal(PhaseGVN *phase, bool can_reshape) {
      ....
      if( op2 == Op_AddL &&
          in2->in(1) == in1 &&
          op1 != Op_ConL &&
          0 ) {
      ....
    }

    PVS-Studio- Warnung : V560 Ein Teil des bedingten Ausdrucks ist immer falsch: 0. addnode.cpp 435

    Es ist ziemlich seltsam, 0 zu verwenden. Im logischen Ausdruck befindet sich dieser Code höchstwahrscheinlich noch in der Entwicklung und zum Debuggen wurde die Bedingung unmöglich gemacht. Der Code enthält keine entsprechenden Kommentare, und in diesem Fall besteht eine hohe Wahrscheinlichkeit, dass Sie vergessen, den Code in Zukunft zu reparieren. Das Ergebnis dieses Fehlers ist, dass alles, was sich in dieser Bedingung befindet, niemals ausgeführt wird, da das Ergebnis der Auswertung eines logischen Ausdrucks immer falsch ist.

    Betriebsprioritäten


    Häufig verlassen sich Programmierer auf ihr Wissen über Prioritäten und markieren die Komponenten eines komplexen Ausdrucks nicht in eckigen Klammern:
    int method_size() const
      { return sizeof(Method)/wordSize + is_native() ? 2 : 0; }

    Warnung PVS-Studio: V502 Vielleicht arbeitet der Operator '?:' Anders als erwartet. Der Operator '?:' Hat eine niedrigere Priorität als der Operator '+'.

    In diesem Fall sind mir die Einzelheiten des Codes nicht bekannt, aber es besteht der Verdacht, dass die geplante Funktion den Versatzwert '2' oder '0' abhängig vom Ergebnis des Aufrufs der Funktion is_native () auswählt, der Ausdruck jedoch eine andere Berechnungsreihenfolge hat. Zuerst wird sizeof (Methode) / wordSize + is_native () hinzugefügt und dann wird das Ergebnis 2 oder 0 zurückgegeben. Der Code sollte höchstwahrscheinlich so aussehen:
    { return sizeof(Method)/wordSize + (is_native() ? 2 : 0); }

    Dies ist ein sehr häufiger Fehler bei der Betriebspriorität. In der Datenbank der vom Analysator gefundenen Fehler wurden die beliebtesten identifiziert und im Artikel: Logische Ausdrücke in C / C ++ angegeben. Wie falsch liegen die Profis .

    Kopieren Einfügen




    Die folgende charakteristische Fehlergruppe bezieht sich auf das Kopieren von Code. Diesem Lieblingstrick der Programmierer kann man sich nicht entziehen. Wir untersuchen daher die Stellen, an denen das Kopieren und Einfügen angewendet wurde:
    static int
    setImageHints(....)
    {
      ....
      if (dstCMP->isDefaultCompatCM) {
          hintP->allocDefaultDst = FALSE;
          hintP->cvtToDst = FALSE;
      }
      else if (dstCMP->isDefaultCompatCM) {
          hintP->allocDefaultDst = FALSE;
          hintP->cvtToDst = FALSE;
      }
      ....
    }

    PVS-Studio- Warnung : V517 Die Verwendung des Musters 'if (A) {...} else if (A) {...}' wurde erkannt. Es ist wahrscheinlich, dass ein logischer Fehler vorliegt. Überprüfen Sie die Zeilen: 1873, 1877. awt_ImagingLib.c 1873

    In diesem Beispiel stimmen sowohl die Bedingungen im if als auch sonst vollständig mit dem Code überein, der ausgeführt werden soll. Die zweite Bedingung ist völlig bedeutungslos, sie wird niemals erfüllt.

    Ein weiterer ähnlicher Fall:
    static int expandPackedBCR(JNIEnv *env, RasterS_t *rasterP, 
                               int component,
                               unsigned char *outDataP)
    {
      ....
      /* Convert the all bands */
      if (rasterP->numBands < 4) {
          /* Need to put in alpha */
          for (y=0; y < rasterP->height; y++) {
              inP = lineInP;
              for (x=0; x < rasterP->width; x++) {
                  for (c=0; c < rasterP->numBands; c++) {
                      *outP++ = (unsigned char)
                          (((*inP&rasterP->sppsm.maskArray[c]) >> roff[c])
                           <scanlineStride;
          }
      }
      else {
          for (y=0; y < rasterP->height; y++) {
              inP = lineInP;
              for (x=0; x < rasterP->width; x++) {
                  for (c=0; c < rasterP->numBands; c++) {
                      *outP++ = (unsigned char)
                          (((*inP&rasterP->sppsm.maskArray[c]) >> roff[c])
                           <scanlineStride;
          }
      }
      ....
    }

    PVS-Studio Warnung: V523 Die Anweisung 'then' entspricht der Anweisung 'else'. awt_ImagingLib.c 2927 Der

    ausführbare Code in beiden Blöcken ist identisch, es gibt keinen Unterschied, was in der Bedingung berechnet wird. Es ist sinnvoll, sich diese Stelle anzusehen und eine unnötige Verzweigung zu entfernen oder, falls eine andere Logik impliziert wurde, den Code anzupassen, um Doppelungen zu vermeiden.

    Es gibt zwei weitere Stellen mit identischer Vervielfältigung. Ich werde sie nur ohne Code darauf hinweisen:
    • V523 Die Anweisung 'then' entspricht der Anweisung 'else'. awt_ImagingLib.c 3111
    • V523 Die Anweisung 'then' entspricht der Anweisung 'else'. awt_ImagingLib.c 3307


    Nun, das letzte interessante Beispiel bezog sich auf einen möglichen Fehler beim Kopieren und Einfügen:

    Node* GraphKit::record_profiled_receiver_for_speculation(Node* n)
    {
      ....
      ciKlass* exact_kls = profile_has_unique_klass();
      bool maybe_null = true;
      if (java_bc() == Bytecodes::_checkcast ||
          java_bc() == Bytecodes::_instanceof ||
          java_bc() == Bytecodes::_aastore) {
        ciProfileData* data = 
          method()->method_data()->bci_to_data(bci());
        bool maybe_null = data == NULL ? true :    <==
                          data->as_BitData()->null_seen();
      }
      return record_profile_for_speculation(n, 
        exact_kls, maybe_null);
      return n;
    }

    PVS-Studio Warnung: V561 Es ist wahrscheinlich besser, der Variablen 'maybe_null' einen Wert zuzuweisen, als ihn neu zu deklarieren. Vorherige Deklaration: graphKit.cpp, Zeile 2170. graphKit.cpp 2175

    Was passiert in diesem Code? Vor dem if- Block wird die Variable bool maybe_null = true deklariert . . Wenn der Code dann im if- Block ausgeführt wird , wird eine Variable mit demselben Namen deklariert. Nach dem Verlassen des Blocks geht der Wert dieser Variablen verloren, und ein Aufruf der Funktion, die diese Variable verwendet, ist in jedem Fall wahr . Nun, wenn diese Variable zu Debug-Zwecken dupliziert wurde. Andernfalls wird dieser Code nicht korrekt ausgeführt und muss geändert werden:
    maybe_null = data == NULL ? true :    
                 data->as_BitData()->null_seen();

    Arbeiten Sie mit Zeigern




    Die Arbeit mit Zeigern erfordert Sorgfalt und Genauigkeit, da die unsachgemäße Verwendung von Zeigern zu schwer identifizierbaren Fehlern führen kann. In der Regel besteht die Hauptgefahr in der Verwendung defekter Zeiger oder der Verwendung von Zeigern ohne Überprüfung auf einen Nullwert.

    Betrachten Sie zunächst den Fall der expliziten Verwendung eines Nullzeigers:
    static jint JNICALL
    cbObjectTagInstance(....)
    {
        ClassInstancesData  *data;
        /* Check data structure */
        data = (ClassInstancesData*)user_data;
        if (data == NULL) {
            data->error = AGENT_ERROR_ILLEGAL_ARGUMENT;
            return JVMTI_VISIT_ABORT;
        }
      ....
    }

    PVS-Studio Warnung: V522 Es kann zu einer Dereferenzierung des Nullzeigers "Daten" kommen. util.c 2424

    Absolut unverständlicher Code mit einem Nullzeiger kann zu einem Programmabsturz führen. Vielleicht hat dieser Zweig nie funktioniert, wodurch es möglich war, Probleme beim Ausführen des Programms zu vermeiden. In derselben Datei befanden sich drei weitere ähnliche Stellen:
    • V522 Möglicherweise findet eine Dereferenzierung des Nullzeigers 'data' statt. util.c 2543
    • V522 Möglicherweise findet eine Dereferenzierung des Nullzeigers 'data' statt. util.c 2601
    • V522 Möglicherweise findet eine Dereferenzierung des Nullzeigers 'data' statt. util.c 2760


    In den folgenden Fällen wird die Möglichkeit der Verwendung eines Nullzeigers jedoch tiefer verborgen. Dies ist eine sehr häufige Situation. Solche Warnungen sind in fast allen geprüften Projekten zu finden:
    static jboolean
    visibleClasses(PacketInputStream *in, PacketOutputStream *out)
    {
      ....
      else {
        (void)outStream_writeInt(out, count);
        for (i = 0; i < count; i++) {
          jbyte tag;
          jclass clazz;
          clazz = classes[i];                     <==
          tag = referenceTypeTag(clazz);
          (void)outStream_writeByte(out, tag);
          (void)outStream_writeObjectRef(env, out, clazz);
        }
      }
      if ( classes != NULL )                      <==
        jvmtiDeallocate(classes);
      ....
        return JNI_TRUE;
    }
    

    PVS-Studio Warnung : V595 Der 'Klassen'-Zeiger wurde verwendet, bevor er gegen nullptr verifiziert wurde. Kontrollzeilen: 58, 66. ClassLoaderReferenceImpl.c 58

    Im unteren Block wird der Zeiger auf einen Nullwert überprüft, was bedeutet, dass der Programmierer die Möglichkeit einräumt, dass der Zeigerwert Null ist. Im obigen Block wird der Zeiger jedoch ohne Überprüfung verwendet. Wenn also der Zeigerwert Null ist, hilft uns eine solche Überprüfung nicht, und wir erhalten eine abnormale Beendigung des Programms. Um diesen Fehler zu beheben, müssen Sie den Zeiger über beiden Blöcken überprüfen.

    Lassen Sie mich Ihnen noch ein ähnliches Beispiel geben:
    int InstructForm::needs_base_oop_edge(FormDict &globals) const {
      if( is_simple_chain_rule(globals) ) {
        const char *src = _matrule->_rChild->_opType;
        OperandForm *src_op = globals[src]->is_operand();
        assert( src_op, "Not operand class of chain rule" );
        return src_op->_matrule ? 
               src_op->_matrule->needs_base_oop_edge() : 0;
      }                             // Else check instruction
      return _matrule ? _matrule->needs_base_oop_edge() : 0;
    }

    PVS-Studio Warnung: V595 Der Zeiger '_matrule' wurde verwendet, bevor er gegen nullptr verifiziert wurde. Check lines: 3534, 3540. formssel.cpp 3534

    Hier wird der Zeiger unten im ternären Operator - _matrule? _matrule-> needs_base_oop_edge (): 0; Und oben gibt es einen einfachen Aufruf dieses Zeigers - const char * src = _matrule -> _ rChild -> _ opType; Das Rezept für die Korrektur ist ähnlich: Sie müssen den Zeiger überprüfen, bevor Sie ihn verwenden. Es gab eine ganze Reihe solcher Orte, ich werde sie auflisten:
    • V595 Der Zeiger '_pipeline' wurde verwendet, bevor er gegen nullptr verifiziert wurde. Überprüfen Sie die Zeilen: 3265, 3274. output_c.cpp 3265
    • V595 Der Zeiger 'index_bound' wurde verwendet, bevor er gegen nullptr verifiziert wurde. Überprüfen Sie die Zeilen: 790, 806. c1_RangeCheckElimination.cpp 790
    • V595 Der Zeiger 'g_type_init' wurde verwendet, bevor er gegen nullptr verifiziert wurde. Zeilen überprüfen: 94, 108. GioFileTypeDetector.c 94
    • V595 Der Zeiger 'classArray' wurde verwendet, bevor er gegen nullptr verifiziert wurde. Check lines: 1169, 1185. JPLISAgent.c 1169
    • V595 Der Zeiger 'q' wurde verwendet, bevor er gegen nullptr verifiziert wurde. Zeilen überprüfen: 594, 599. mpi.c 594
    • V595 Der Zeiger 'info.waiters' wurde verwendet, bevor er gegen nullptr verifiziert wurde. Zeilen überprüfen: 224, 228. ObjectReferenceImpl.c 224
    • V595 Der Zeiger 'methods' wurde verwendet, bevor er gegen nullptr verifiziert wurde. Zeilen überprüfen: 225, 229. ReferenceTypeImpl.c 225
    • V595 Der Zeiger 'Felder' wurde verwendet, bevor er gegen nullptr verifiziert wurde. Zeilen prüfen: 433, 437. ReferenceTypeImpl.c 433
    • V595 Der 'verschachtelte' Zeiger wurde verwendet, bevor er gegen nullptr verifiziert wurde. Zeilen prüfen: 538, 540. ReferenceTypeImpl.c 538
    • V595 Der Zeiger 'interfaces' wurde verwendet, bevor er gegen nullptr verifiziert wurde. Zeilen überprüfen: 593, 595. ReferenceTypeImpl.c 593
    • V595 Der Zeiger 'buf' wurde verwendet, bevor er gegen nullptr verifiziert wurde. Zeilen prüfen: 265, 266. ps_proc.c 265
    • V595 Der Zeiger 'Monitor' wurde verwendet, bevor er gegen nullptr verifiziert wurde. Zeilen überprüfen: 382, ​​387. ThreadReferenceImpl.c 382
    • V595 Der Zeiger 'Monitor' wurde verwendet, bevor er gegen nullptr verifiziert wurde. Zeilen prüfen: 557, 560. ThreadReferenceImpl.c 557
    • V595 Der Zeiger 'Signatur' wurde verwendet, bevor er gegen nullptr verifiziert wurde. Überprüfen Sie die Zeilen: 520, 526. debugInit.c 520
    • V595 Der 'BlackPoint'-Zeiger wurde verwendet, bevor er gegen nullptr verifiziert wurde. Überprüfen Sie die Zeilen: 192, 208. cmssamp.c 192
    • V595 Der Zeiger 'ursprünglicher Name' wurde verwendet, bevor er gegen nullptr verifiziert wurde. Zeilen prüfen: 506, 511. awt_Font.c 506
    • V595 Der Zeiger 'pseq-> seq' wurde verwendet, bevor er gegen nullptr verifiziert wurde. Zeilen überprüfen: 788, 791. cmsnamed.c 788
    • V595 Der Zeiger 'GammaTables' wurde verwendet, bevor er gegen nullptr verifiziert wurde. Check lines: 1430, 1434. cmsopt.c 1430


    Im Gegensatz dazu überprüfen Programmierer manchmal Zeiger, machen es aber falsch:
    FileBuff::FileBuff( BufferedFile *fptr, ArchDesc& archDesc) : 
                       _fp(fptr), _AD(archDesc) {
      ....
      _bigbuf = new char[_bufferSize];
      if( !_bigbuf ) {
        file_error(SEMERR, 0, "Buffer allocation failed\n");
        exit(1);
      ....
    }

    PVS-Studio Warnung: V668 Es hat keinen Sinn, den Zeiger '_bigbuf' gegen null zu testen, da der Speicher mit dem Operator 'new' zugewiesen wurde. Die Ausnahme wird im Fall eines Speicherzuordnungsfehlers generiert. filebuff.cpp 47

    In diesem Fall ist es sinnlos, den _bigbuf-Zeiger nach der Verwendung von new auf einen Nullwert zu überprüfen. Wenn das System keinen Speicher zuweist, wird eine Ausnahme generiert und die Funktion wird nicht mehr ausgeführt. Um den Fehler zu beheben, können Sie verschiedene Ansätze verwenden. Nehmen Sie die Speicherzuweisung im try catch-Block vor , oder verwenden Sie das neue (std :: nothrow) -Konstrukt , um Speicher zuzuweisen , der bei einem Fehler keine Ausnahme auslöst . Es gibt mehrere solcher falschen Prüfungen:
    • V668 Es hat keinen Sinn, den Zeiger 'vspace' gegen null zu testen, da der Speicher mit dem Operator 'new' zugewiesen wurde. Die Ausnahme wird im Fall eines Speicherzuordnungsfehlers generiert. psParallelCompact.cpp 455
    • V668 Es hat keinen Sinn, den Zeiger 'uPtr' gegen Null zu testen, da der Speicher mit dem Operator 'new' zugewiesen wurde. Die Ausnahme wird im Fall eines Speicherzuordnungsfehlers generiert. jni.cpp 113


    Der letzte Fehler bei der Arbeit mit Zeigern wurde beim expliziten Umsetzen eines Zeigers eines Typs auf einen Zeiger eines anderen Typs gemacht:
    mlib_status mlib_convMxNext_f32(...)
    {
      mlib_d64 dspace[1024], *dsa = dspace;
      ....
      mlib_f32 *fsa;
      ....
      if (3 * wid_e + m > 1024) {
        dsa = mlib_malloc((3 * wid_e + m) * sizeof(mlib_d64));
        if (dsa == NULL)
          return MLIB_FAILURE;
      }
      fsa = (mlib_f32 *) dsa; <==
      ....
    }

    PVS-Studio- Warnung: V615 Eine ungewöhnliche explizite Konvertierung vom Typ 'double *' in den Typ 'float *'. mlib_ImageConvMxN_Fp.c 294

    Zeiger auf float mlib_f32 * fsa versucht, einen Zeiger auf double mlib_d64 dspace [1024] zuzuweisen , * dsa = dspace . Die Typen float und double haben unterschiedliche Größen, und eine ähnliche Typumwandlung ist höchstwahrscheinlich falsch. Die Größeninkongruenz der zu übersetzenden Typen führt dazu , dass der fsa- Zeiger das Zahlenformat angibt, das für den Float-Typ falsch ist.

    In einer anderen Datei gibt es zwei weitere ähnliche Konvertierungen. Sie müssen diesen Code sorgfältig prüfen und die richtigen Typkonvertierungen verwenden.
    • V615 Eine ungewöhnliche explizite Konvertierung vom Typ 'double *' in den Typ 'float *'. mlib_ImageLookUp_Bit.c 525
    • V615 Eine ungewöhnliche explizite Konvertierung vom Typ 'double *' in den Typ 'float *'. mlib_ImageLookUp_Bit.c 526

    Dies schließt unsere Erörterung von Fehlern im Zusammenhang mit der Verwendung von Zeigern ab und untersucht die verbleibenden Warnungen des Analysegeräts.

    Verschiedene Fehler




    Der folgende Fehler ist wahrscheinlich auch das Ergebnis eines nicht erfolgreichen Kopierens des Codes:
    static bool
    parse_bool (const char **pp, const char *end, unsigned int *pv)
    {
      ....
      /* CSS allows on/off as aliases 1/0. */
      if (*pp - p == 2 || 0 == strncmp (p, "on", 2))
        *pv = 1;
      else if (*pp - p == 3 || 0 == strncmp (p, "off", 2))
        *pv = 0;
      else
        return false;
      return true;
    }

    PVS-Studio Warnung: V666 Betrachten Sie das dritte Argument der Funktion 'strncmp'. Möglicherweise entspricht der Wert nicht der Länge eines Strings, der mit dem zweiten Argument übergeben wurde. hb-shape.cc 104 Dies

    ist nur der Fall, wenn der Fehler die Leistung des Programms nicht beeinträchtigt. Anstatt die drei Zeichen zu vergleichen, werden nur die ersten beiden verglichen. Ich schließe jedoch nicht aus, dass der Autor des Codes eine solche Prüfung durchführen kann und dies auch konkret getan hat. Da der Wert im Puffer p ein oder aus sein kann, ist es ausreichend, die ersten beiden Zeichen zu vergleichen. Der Ordnung halber können Sie den Code aber noch korrigieren:
    else if (*pp - p == 3 || 0 == strncmp (p, "off", 3))

    Es gab mehrere Stellen, an denen mögliche Fehler bei der Implementierung von Klassen auftraten:
    class ProductionState {
      ....
    private:
        // Disable public use of constructor, copy-ctor,  ...
      ProductionState( )                         :
      _production(cmpstr, hashstr, Form::arena) 
      {  assert( false, "NotImplemented");  };
      ProductionState( const ProductionState & ) :
      _production(cmpstr, hashstr, Form::arena) 
      {  assert( false, "NotImplemented");  }; // Deep-copy
    };

    PVS-Studio- Warnung : Der V690-Kopierkonstruktor ist in der 'ProductionState'-Klasse als privat deklariert, der Standardoperator' = 'wird jedoch weiterhin vom Compiler generiert. Es ist gefährlich, eine solche Klasse zu benutzen. dfa.cpp 76

    In dieser Klasse haben sie versucht, das Kopieren zu verbieten, aber vergessen, dem privaten Bereich einen Kopieroperator hinzuzufügen. Es wird standardmäßig generiert und steht zur Verfügung. Auch wenn dieser Operator jetzt nirgendwo im Code verwendet wird, gibt es keine Garantie dafür, dass er in Zukunft nicht versehentlich von jemandem aufgerufen wird. Wenn ein solcher Operator aufgerufen wird, wird eine Kopie für Kopie für eine Klasse erstellt, die nicht kopiert werden soll. Dies kann zu verschiedenen Effekten führen, bis das Programm abstürzt. In diesem Fall sollte die Operatordeklaration "=" zum privaten Bereich hinzugefügt werden.

    Es gibt zwei weitere Klassen, in denen es ähnliche Probleme gibt. Es ist wünschenswert, sie so anzupassen, dass sie nicht gegen das " Gesetz der Großen Zwei " verstoßen .
    • V690 Die Klasse 'MemRegion' implementiert einen Kopierkonstruktor, es fehlt jedoch der Operator '='. Es ist gefährlich, eine solche Klasse zu benutzen. memRegion.hpp 43
    • Der V690-Kopierkonstruktor ist in der Klasse 'Label' als privat deklariert, der Standardoperator '=' wird jedoch weiterhin vom Compiler generiert. Es ist gefährlich, eine solche Klasse zu benutzen. assembler.hpp 73


    Der letzte Fehler ist wie ein einfacher Tippfehler:
    bool os::start_debugging(char *buf, int buflen) {
      int len = (int)strlen(buf);
      char *p = &buf[len];
      ....
      if (yes) {
        // yes, user asked VM to launch debugger
        jio_snprintf(buf, sizeof(buf), "gdb /proc/%d/exe %d",
          os::current_process_id(), os::current_process_id());
        os::fork_and_exec(buf);
        yes = false;
      }
      return yes;
    }

    PVS-Studio Warnung: V579 Die Funktion jio_snprintf empfängt den Zeiger und seine Größe als Argumente. Es ist möglicherweise ein Fehler. Überprüfen Sie das zweite Argument. os_linux.cpp 6094

    Der Programmierer wollte die Länge des Puffers übergeben, berücksichtigte jedoch nicht, dass es sich nicht um ein lokal deklariertes Array handelt, sondern um einen Zeiger, der im Funktionsargument enthalten ist. Als Ergebnis der Auswertung des Ausdrucks sizeof (buf) erhalten wir nicht die Länge des Puffers, sondern die Größe des Zeigers, die 4 oder 8 Bytes beträgt. Es ist einfach, den Fehler zu beheben, da die Pufferlänge bereits ermittelt wurde: int len ​​= (int) strlen (buf); Die richtige Option würde folgendermaßen aussehen:
    jio_snprintf(buf, len ....

    Fazit




    Es ist immer interessant, ein Projekt zu überprüfen, das viele Leute beschäftigt und dessen Qualität überwacht. Es wurde eine relativ große Anzahl von Fehlern gefunden, in diesem Artikel wird nur eine bestimmte Anzahl von Fehlern beschrieben, der Rest erfordert eingehendere Recherchen. Die gefundenen Fehler bestätigen erneut die Wirksamkeit der Verwendung eines statischen Analysegeräts, da Sie Fehler erkennen können, die bei Betrachtung mit einem einfachen Auge schwierig zu erkennen sind. Es ist am effektivsten, den Analysator kontinuierlich zu verwenden, da dies viel Zeit spart, die für das Debuggen des Programms bei der Suche nach Fehlern aufgewendet wird. Ich erinnere Sie daran, dass Sie die Arbeit des statischen Analysators PVS-Studio an Ihrem Projekt ausprobieren können, indem Sie dessen Testversion herunterladen .


    Wenn Sie diesen Artikel mit einem englischsprachigen Publikum teilen möchten, verwenden Sie bitte den Link zur Übersetzung: Svyatoslav Razmyslov. OpenJDK-Check von PVS-Studio .

    Haben Sie den Artikel gelesen und eine Frage?
    Oft werden unseren Artikeln die gleichen Fragen gestellt. Wir haben die Antworten hier gesammelt: Antworten auf Fragen von Lesern von Artikeln über PVS-Studio, Version 2015 . Bitte beachten Sie die Liste.

    Jetzt auch beliebt: