ユニコーンはKDEに興味を持っています



KDE(K Desktop Environmentの略)は、主にLinuxおよびその他のUNIXライクシステム用のデスクトップ環境です。 簡単に言えば、これがすべてのグラフィックデザインの責任です。 この環境は、クロスプラットフォームのQtユーザーインターフェイス開発ツールに基づいて構築されています。 フリーソフトウェアのアイデアに専念している世界中の数百人のプログラマーが開発に従事しています。 KDEは、最新のグラフィカルインターフェイスでオペレーティングシステムと対話できるユーザー環境アプリケーションの完全なセットを提供します。 KDEの内部を見てみましょう。



KDEプロジェクトバージョン4.14の以下のパッケージは、 OpenSUSE FactoryでPVS-Studio 5.19を使用してテストされました。

KDE PIMライブラリ



V547式は常に真です。 ここでは、おそらく「&&」演算子を使用する必要があります。 occurrenceformatter.cpp 2684

enum PartStat { .... Accepted, Tentative, .... }; static QString formatICalInvitationHelper(....) { .... a = findDelegatedFromMyAttendee( inc ); if ( a ) { if ( a->status() != Attendee::Accepted || //<== a->status() != Attendee::Tentative ) { //<== html += responseButtons( inc, rsvpReq, rsvpRec, helper ); break; } } .... }
      
      





式は常に真です。 理由は、タイプミスまたはプログラマロジックの誤りです。 おそらく、ここで「&&」演算子を使用する必要があります。



同様の不審な場所:

V593 「A = B == C」という表現を検討することを検討してください。 式は、「A =(B == C)」のように計算されます。 kio_ldap.cpp 535

 void LDAPProtocol::del( const KUrl &_url, bool ) { .... if ( (id = mOp.del( usrc.dn() ) == -1) ) { LDAPErr(); return; } ret = mOp.waitForResult( id, -1 ); .... }
      
      





比較演算子(==)の優先順位は、割り当て演算子(=)の優先順位よりも高くなっています。 運のおかげで、条件は計画どおりに満たされますが、0に等しいid idの誤った値が使用されます。



V595 nullptrに対して検証される前に、「incBase」ポインターが使用されました。 行をチェック:2487、2491。occurrenceformatter.cpp 2487

 static QString formatICalInvitationHelper(....) { .... incBase->shiftTimes( mCalendar->timeSpec(), ....); Incidence *existingIncidence = 0; if ( incBase && helper->calendar() ) { .... } .... }
      
      





'incBase'ポインターの逆参照は、チェックする前に行われます。



V622 「switch」ステートメントの検査を検討してください。 最初の「ケース」演算子が欠落している可能性があります。 listjob.cpp 131

 void ListJob::doStart() { Q_D( ListJob ); switch ( d->option ) { break; //<== case IncludeUnsubscribed: d->command = "LIST"; break; case IncludeFolderRoleFlags: d->command = "XLIST"; break; case NoOption: default: d->command = "LSUB"; } .... }
      
      





ステートメントブロック「switch」では、最初のステートメントは「case」ではありません。 これは、コードフラグメントが制御を取得しないという事実につながります。 最良の場合、「break」ステートメントは古い条件が完全に削除された後も残りますが、最悪の場合、ここでは別の「case」が省略されます。



V701 realloc()リークの可能性:realloc()がメモリの割り当てに失敗すると、元のポインタ「lexBuf.strs」が失われます。 realloc()を一時ポインターに割り当てることを検討してください。 vcc.y 638

 static void lexAppendc(int c) { lexBuf.strs = (char *) realloc(lexBuf.strs, (size_t) .... + 1); lexBuf.strs[lexBuf.strsLen] = c; .... }
      
      





この式は潜在的に危険です。realloc関数の結果を別の変数に格納することをお勧めします。 realloc()関数は、メモリブロックのサイズを変更します。 エラーが発生した場合、古いメモリ領域へのポインタは失われます。



とにかく、そのようなコードは悪いです。 realloc()関数が返したものの検証はありません。 ポインタは次の行ですぐに逆参照されます。



同様の場所:

KDEベースライブラリ



V523 「then」ステートメントは「else」ステートメントと同等です。 kconfig_compiler.cpp 1051

 QString newItem( const QString &type, ....) { QString t = "new "+cfg.inherits+"::Item" + ....; if ( type == "Enum" ) t += ", values" + name; if ( !defaultValue.isEmpty() ) { t += ", "; if ( type == "String" ) t += defaultValue; //<== else t+= defaultValue; //<== } t += " );"; return t; }
      
      





ifステートメントの同じtrueおよびfalseブランチは非常に疑わしく見えます。 コードに誤字が含まれていない場合は、たとえば次のように簡略化できます。

 if ( !defaultValue.isEmpty() ) t += ", " + defaultValue;
      
      





同様の場所:

V595 nullptrに対して検証される前に、「 priv- >スライダー」ポインターが使用されました。 行をチェック:786、792。knuminput.cpp 786

 void KDoubleNumInput::spinBoxChanged(double val) { .... const double slidemin = priv->slider->minimum(); //<== const double slidemax = priv->slider->maximum(); //<== .... if (priv->slider) { //<== priv->slider->blockSignals(true); priv->slider->setValue(qRound(slidemin + rel * (....))); priv->slider->blockSignals(false); } }
      
      





「priv」ポインターの逆参照は、チェックする前に行われます。



同様の危険スポット:

V646アプリケーションのロジックの検査を検討してください。 「else」キーワードが欠落している可能性があります。 karchive.cpp 187

 *bool KArchive::close() { .... // if d->saveFile is not null then it is equal to d->dev. if ( d->saveFile ) { closeSucceeded = d->saveFile->finalize(); delete d->saveFile; d->saveFile = 0; } if ( d->deviceOwned ) { //<== delete d->dev; // we created it ourselves in open() } .... }
      
      





このコードスニペットは、「else」キーワードが欠落していること、または非常に見苦しくて混乱を招くコード設計であることを示している場合があります。



V655ストリングは連結されましたが、使用されません。 式を調べることを検討してください。 entrydetailsdialog.cpp 225

 void EntryDetails::updateButtons() { .... foreach (....) { QString text = info.name; if (!info.distributionType.trimmed().isEmpty()) { text + " (" + info.distributionType.trimmed() + ")";//<== } QAction* installAction = installMenu->addAction(KIcon("dialog-ok"), text); installAction->setData(info.id); } .... }
      
      





アナライザーは、未使用のストリング変数共用体を検出しました。 おそらく、コードは次のようになります。

 text += " (" + info.distributionType.trimmed() + ")";
      
      





同様の場所:

V705 「else」ブロックが忘れられているかコメントアウトされている可能性があり、そのためプログラムの動作ロジックが変更されています。 entrydetailsdialog.cpp 149

 void EntryDetails::entryChanged(const KNS3::EntryInternal& entry) { .... if(m_entry.previewUrl(EntryInternal::PreviewSmall1).isEmpty()){ ui->previewBig->setVisible(false); } else //<== if (!m_entry.previewUrl((....)type).isEmpty()) { .... } .... }
      
      





このコードフラグメントの設計もあいまいに見えます。 「else if」コンストラクトはここで計画されましたか、または「else if」の削除を忘れましたか?



V612ループ内の無条件の「戻り」。 bufferfragment_p.h 94

 BufferFragment split(char c, unsigned int* start) { while (*start < len) { int end = indexOf(c, *start); if (end == -1) end = len; BufferFragment line(d + (*start), end - (*start)); *start = end + 1; return line; } return BufferFragment(); }
      
      





1回の繰り返しのためにループを書く価値がありましたか? それとも、条件付きステートメントがないのですか?



V701 realloc()リークの可能性:realloc()がメモリの割り当てに失敗すると、元のポインタ「d」が失われます。 realloc()を一時ポインターに割り当てることを検討してください。 netwm.cpp 596

 template <class Z> void NETRArray<Z>::reset() { sz = 0; capacity = 2; d = (Z*) realloc(d, sizeof(Z)*capacity); memset( (void*) d, 0, sizeof(Z)*capacity ); }
      
      





KDE PIMライブラリのように、メモリを拡張できない場合、古いメモリ領域へのポインタが失われる可能性があるため、realloc()関数で単一のポインタを使用することは推奨されません。



同様の場所:

KDEベースアプリ



V501 「&&」演算子の左と右に同じサブ式「mimeData-> hasFormat(QLatin1String( "application / x-kde-ark-dndextract-service"))」があります。 iconview.cpp 2357

 void IconView::dropEvent(QGraphicsSceneDragDropEvent *event) { .... if (mimeData->hasFormat(QLatin1String( "application/x-kde-ark-dndextract-service")) && //<== mimeData->hasFormat(QLatin1String( "application/x-kde-ark-dndextract-service"))) //<== { const QString remoteDBusClient = mimeData->data( QLatin1String("application/x-kde-ark-dndextract-service")); const QString remoteDBusPath = mimeData->data( QLatin1String("application/x-kde-ark-dndextract-path")); .... } .... }
      
      





アナライザーは同じ条件式を検出しました。 条件演算子の本体では、条件が次のようになっていると仮定するのが論理的です。

 if (mimeData->hasFormat(QLatin1String( "application/x-kde-ark-dndextract-service")) && //<== mimeData->hasFormat(QLatin1String( "application/x-kde-ark-dndextract-path "))) //<== { .... }
      
      





V595 nullptrに対して検証される前に、「m_view」ポインターが使用されました。 行を確認してください:797、801。kitemlistcontroller.cpp 797

 bool KItemListController::mouseDoubleClickEvent(....) { const QPointF pos = transform.map(event->pos()); const int index = m_view->itemAt(pos); // Expand item if desired - See Bug 295573 if (m_mouseDoubleClickAction != ActivateItemOnly) { if (m_view && m_model && ....) { const bool expanded = m_model->isExpanded(index); m_model->setExpanded(index, !expanded); } } .... }
      
      





KDEプロジェクトでは、関数に入ってきたポインターが最初にローカル変数を初期化するために使用され、後で参照解除される前にチェックされる多くの場所がありました。



V637反対の2つの条件が発生しました。 2番目の条件は常にfalseです。 チェック行:410、412。kebsearchline.cpp 410

 void KViewSearchLine::slotColumnsRemoved(const QModelIndex &, int first, int last) { if(d->treeView) updateSearch(); else { if(d->listView->modelColumn() >= first && d->listView->modelColumn() <= last) { if(d->listView->modelColumn()>last) //<== kFatal()<<"...."<<endl; updateSearch(); } } }
      
      





ネストされた条件は常にfalseです。 この条件は、いくつかの編集まで関連していたと想定できます。



V654ループの条件 'state!= 1'は常に真です。 passwd.cpp 255

 int PasswdProcess::ConversePasswd(....) { .... state = 0; while (state != 1) { line = readLine(); if (line.isNull()) { // No more input... OK return 0; } if (isPrompt(line, "password")) { // Uh oh, another prompt. Not good! kill(m_Pid, SIGKILL); waitForChild(); return PasswordNotGood; } m_Error += line + '\n'; // Collect error message } .... }
      
      





変数 'state'の値はループ内で変化しないため、停止条件は 'return'の呼び出しのみです。



KDE開発



V501 「&&」演算子の左側と右側には、同一のサブ式「file == rhs.file」があります。 pp-macro.cpp 44

 bool pp_macro::operator==(const pp_macro& rhs) const { if(completeHash() != rhs.completeHash()) return false; return name == rhs.name && file == rhs.file && //<== file == rhs.file && //<== sourceLine == rhs.sourceLine && defined == rhs.defined && hidden == rhs.hidden && function_like == rhs.function_like && variadics == rhs.variadics && fixed == rhs.fixed && defineOnOverride == rhs.defineOnOverride && listsEqual(rhs); }
      
      





アナライザーは、条件式が繰り返される複数の場所を検出しました。 これのいくつかは深刻なタイプミスかもしれません。



このような他の場所:

V595 nullparentに対して検証される前に、「parentJob()-> cpp()」ポインターが使用されました。 行を確認してください:437、438。cppparsejob.cpp 437

 void CPPInternalParseJob::run() { .... QReadLocker lock(parentJob()->parentPreprocessor() ? 0: parentJob()->cpp()->language()->parseLock()); //<== if(.... || !parentJob()->cpp()) //<== return; .... }
      
      





そして、このプロジェクトには、チェックする前にポインターを間接参照する場所がありました。



別の場所:

V564 「&」演算子はブール型の値に適用されます。 括弧を含めるのを忘れているか、「&&」演算子を使用することを意図している可能性があります。 usedecoratorvisitor.cpp 40

 DataAccess::DataAccessFlags typeToDataAccessFlags(....) { DataAccess::DataAccessFlags ret = DataAccess::Read; TypePtr< ReferenceType > reftype=type.cast<ReferenceType>(); if(reftype && reftype->baseType() && !reftype->baseType()->modifiers() & //<== AbstractType::ConstModifier) ret |= DataAccess::Write; return ret; }
      
      





著者は、エラーがここにあるかどうかをよく知っていますが、「&」演算子は疑わしいように見えます。 式 "!Reftype-> baseType()-> modifiers()"は、タイプが 'bool'であることに注意してください。



V555式「m_pos-backOffset> 0」は「m_pos!= BackOffset」として機能します。 pp-stream.cpp 225

 unsigned int rpp::Stream::peekLastOutput(uint backOffset) const { if(m_pos - backOffset > 0) return m_string->at(m_pos - backOffset - 1); return 0; }
      
      





負の結果は非常に大きな正の数として解釈される可能性があるため、符号なしの数の差をゼロと比較することは完全に正しいとは限りません。 条件本体に巨大なインデックスを取得しないようにするには、次のように条件を記述することをお勧めします。

 if(m_pos > backOffset) return m_string->at(m_pos - backOffset - 1);
      
      





同様の場所:

おわりに



KDE製品のユーザーと開発者の大勢の聴衆はテストの面で大きな役割を果たしますが、さまざまなコードアナライザーに注意を払う価値もあります。 ただし、インターネット上の出版物から判断すると、少なくともCoverityはすでにKDEソースコードをチェックするために使用されています。 これが原因で、PVS-Studioが疑わしい場所をほとんど見つけることができません。



静的分析を定期的に使用することで、より便利なタスクを解決するために多くの時間を節約できます。 また、コードの品質管理は、たとえば、新しいサービスを使用して、C / C ++コードの定期的な監査など、他のサービスに移行できます



この記事は英語です。



英語を話す聴衆とこの記事を共有したい場合は、翻訳へのリンクを使用してください:Svyatoslav Razmyslov。 KDEに興味を持つユニコーン



記事を読んで質問がありますか?
多くの場合、記事には同じ質問が寄せられます。 ここでそれらに対する回答を収集しました: PVS-StudioおよびCppCatバージョン2014に関する記事の読者からの質問への回答 。 リストをご覧ください。




All Articles