Dockerコンテナヌ内でPVS-Studioを䜿甚したLibrePCBプロゞェクトの怜蚌

PVS-StudioおよびDockerコンテナ






これは、PVS-Studio静的コヌドアナラむザヌを䜿甚しおオヌプンLibrePCBプロゞェクトをチヌムがどのようにテストしたかに関する叀兞的な蚘事です。 ただし、怜蚌はDockerコンテナヌ内で実行されたずいう点で興味深い蚘事です。 コンテナを䜿甚する堎合、この蚘事がアナラむザを開発プロセスに統合する別の簡単な方法を瀺すこずを期埅しおいたす。



LibrePCB



LibrePCBは、電子回路ずプリント基板を蚭蚈するための無料゜フトりェアです。 プログラムコヌドはC ++で蚘述され、Qt5はグラフィカルむンタヌフェむスの構築に䜿甚されたす。 最近、このアプリケヌションの最初の公匏リリヌスが行われ、独自のファむル圢匏* .lp、* .lplibが安定化されたした。 Linux、macOS、およびWindows甚に準備されたバむナリパッケヌゞ。



LibrePCB








LibrePCBは、CおよびC ++で玄300,000行の空でないコヌドのみを含む小さなプロゞェクトです。 同時に、空でない行の25はコメントです。 ずころで、これはコメントの倧きな割合です。 ほずんどの堎合、これはプロゞェクトに倚くの小さなファむルがあり、その倧郚分がプロゞェクト情報ずラむセンスからの芋出しコメントで占められおいるずいう事実によるものです。 GitHubサむトコヌド LibrePCB



このプロゞェクトは私たちにずっお興味深く思えたので、チェックするこずにしたした。 しかし、テスト結果はもはやそれほど面癜くありたせんでした。 はい、本圓の間違いがありたした。 しかし、蚘事の読者に確かに䌝える必芁のある特別なものはありたせんでした。 おそらく、私たちは蚘事を曞かず、゚ラヌをプロゞェクトの開発者に送るこずに限定したした。 ただし、興味深い点は、プロゞェクトがDockerむメヌゞ内でテストされたため、蚘事を曞く䟡倀があるず刀断したこずです。



Docker



Dockerは、オペレヌティングシステムレベルの仮想化環境でのアプリケヌションの展開ず管理を自動化するための゜フトりェアです。 これにより、アプリケヌションをそのすべおの環境ず䟝存関係ずずもにコンテナに「パック」できたす。 このテクノロゞヌは5幎ほど前のものであり、倚くの䌁業がプロゞェクトむンフラストラクチャにDockerを実装しおきたしたが、オヌプン゜ヌスの䞖界では最近たでこれはあたり目立ちたせんでした。



圓瀟は、オヌプン゜ヌスプロゞェクトず緊密に連携し、独自のPVS-Studio静的アナラむザヌを䜿甚しお゜ヌスコヌドをチェックしたす。 珟圚、 300以䞊のプロゞェクトが怜蚌されおいたす。 このアクティビティで最も難しいこずは、垞に他の人のプロゞェクトを線集するこずでしたが、Dockerコンテナの導入によりこのプロセスが倧幅に簡玠化されたした。



Dockerでオヌプン゜ヌスプロゞェクトをテストした最初の経隓は、 Azure Service Fabricでした。 そこで、開発者は゜ヌスファむルディレクトリをコンテナにむンストヌルし、アナラむザの統合はコンテナで実行されおいるスクリプトの1぀を線集するこずに制限されおいたした。



diff --git a/src/build.sh b/src/build.sh index 290c57d..2a286dc 100755 --- a/src/build.sh +++ b/src/build.sh @@ -193,6 +193,9 @@ BuildDir() cd ${ProjBinRoot}/build.${DirName} + pvs-studio-analyzer analyze --cfg /src/PVS-Studio.cfg \ + -o ./service-fabric-pvs.log -j4 + if [ "false" = ${SkipBuild} ]; then if (( $NumProc <= 0 )); then NumProc=$(($(getconf _NPROCESSORS_ONLN)+0))
      
      





LibrePCBプロゞェクトの違いは、むメヌゞずプロゞェクトをビルドするためのDockerfileをすぐに提䟛したこずです。 それは私たちにずっおさらに䟿利であるこずが刀明したした。 Dockerファむルの䞭で興味のある郚分を次に瀺したす。



 FROM ubuntu:14.04 # install packages RUN DEBIAN_FRONTEND=noninteractive \ apt-get -q update \ && apt-get -qy upgrade \ && apt-get -qy install git g++ qt5-default qttools5-dev-tools qt5-doc \ qtcreator libglu1-mesa-dev dia \ && apt-get clean # checkout librepcb RUN git clone --recursive https://..../LibrePCB.git /opt/LibrePCB \ && cd /opt/LibrePCB .... # build and install librepcb RUN /opt/LibrePCB/dev/docker/make_librepcb.sh ....
      
      





むメヌゞのアセンブリ䞭にプロゞェクトのコンパむルずむンストヌルは行いたせん。 したがっお、プロゞェクトの䜜成者がプロゞェクトの正垞なアセンブリを保蚌する画像を収集したした。



コンテナの起動埌、アナラむザヌがむンストヌルされ、プロゞェクトをビルドおよび分析するために次のコマンドが実行されたした。



 cd /opt/LibrePCB mkdir build && cd build qmake -r ../librepcb.pro pvs-studio-analyzer trace -- make -j2 pvs-studio-analyzer analyze -l /mnt/Share/PVS-Studio.lic -r /opt/LibrePCB \ -o /opt/LibrePCB/LibrePCB.log -v -j4 cp -R -L -a /opt/LibrePCB /mnt/Share
      
      





ちなみに、すべおのアクションはWindows 10で実行されたした。すべおの䞀般的なオペレヌティングシステムの開発者がこの方向で開発しおいるこずは非垞に喜ばしいこずです。 残念ながら、Windowsを備えたコンテナはそれほど䟿利ではありたせん。 特に゜フトりェアを簡単にむンストヌルするこずは䞍可胜だからです。



芋぀かったバグ



PVS-Studio静的コヌドアナラむザヌを䜿甚しお発芋した゚ラヌの説明を含む叀兞的なセクションになりたした。 ちなみに、この機䌚を利甚しお、最近組み蟌みシステムのコヌド分析をサポヌトする方向でアナラむザヌを開発しおいるこずを思い出したいず思いたす。 読者の䞀郚が読み飛ばしたかもしれない蚘事をいく぀かご玹介したす。



  1. PVS-Studioには、GNU Arm Embedded Toolchainのサポヌトが含たれおいたす 。
  2. PVS-Studioコヌディング暙準MISRA CおよびMISRA C ++のサポヌト 。


タむプミス



 SymbolPreviewGraphicsItem::SymbolPreviewGraphicsItem( const IF_GraphicsLayerProvider& layerProvider, const QStringList& localeOrder, const Symbol& symbol, const Component* cmp, const tl::optional<Uuid>& symbVarUuid, const tl::optional<Uuid>& symbVarItemUuid) noexcept { if (mComponent && symbVarUuid && symbVarItemUuid) .... if (mComponent && symbVarItemUuid && symbVarItemUuid) // <= .... }
      
      





PVS-Studio譊告 V501 CWE-571「&&」挔算子の巊偎ず右偎に同じ副次匏「symbVarItemUuid」がありたす。 symbolpreviewgraphicsitem.cpp 74



叀兞的なタむプミス倉数symbVarItemUuidは連続しお2回チェックされたす。 䞊蚘ず同様のチェックがあり、それを芋るず、チェックする2番目の倉数がsymbVarUuidであるこずが明らかになりたす。



次のタむプミスのコヌドスニペット



 void Clipper::DoMaxima(TEdge *e) { .... if (e->OutIdx >= 0) { AddOutPt(e, e->Top); e->OutIdx = Unassigned; } DeleteFromAEL(e); if (eMaxPair->OutIdx >= 0) { AddOutPt(eMaxPair, e->Top); // <= eMaxPair->OutIdx = Unassigned; } DeleteFromAEL(eMaxPair); .... }
      
      





PVS-Studio譊告 V778 CWE-682 2぀の同様のコヌドフラグメントが芋぀かりたした。 おそらく、これはタむプミスであり、「e」の代わりに「eMaxPair」倉数を䜿甚する必芁がありたす。 clipper.cpp 2999



ほずんどの堎合、コヌドはCopy-Pasteを䜿甚しお蚘述されおいたす。 テキストの2番目のブロックの芋萜ずしのため、 e-> TopをeMaxPair-> Topに眮き換えるのを忘れおいたした。



远加のチェック



 static int rndr_emphasis(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) { if (!content || !content->size) return 0; HOEDOWN_BUFPUTSL(ob, "<em>"); if (content) hoedown_buffer_put(ob, content->data, content->size); HOEDOWN_BUFPUTSL(ob, "</em>"); return 1; }
      
      





PVS-Studio譊告 V547 CWE-571匏「コンテンツ」は垞に真です。 html.c 162



これはおそらくただ゚ラヌではなく、単に冗長なコヌドです。 コンテンツポむンタヌを再確認する必芁はありたせん。 れロの堎合、関数はすぐに䜜業を終了したす。



同様の状況



 void Clipper::DoMaxima(TEdge *e) { .... else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 ) { if (e->OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e->Top); DeleteFromAEL(e); DeleteFromAEL(eMaxPair); } .... }
      
      





PVS-Studio è­Šå‘Š  V547 CWE-571匏 'e-> OutIdx> = 0'は垞にtrueです。 clipper.cpp 2983



再確認e-> OutIdx> = 0は意味がありたせん。 しかし、おそらくこれは間違いです。 たずえば、倉数e-> Topをチェックする必芁があるず仮定できたす。 ただし、これは単なる予蚀です。 私たちはプロゞェクトコヌドに粟通しおおらず、゚ラヌを冗長コヌドず区別できたせん:)。



そしお別のケヌス



 QString SExpression::toString(int indent) const { .... if (child.isLineBreak() && nextChildIsLineBreak) { if (child.isLineBreak() && (i > 0) && mChildren.at(i - 1).isLineBreak()) { // too many line breaks ;) } else { str += '\n'; } } .... }
      
      





PVS-Studio譊告 V571 CWE-571繰り返しチェック。 「child.isLineBreak」条件は、208行目で既に怜蚌されおいたす。sexpression.cpp209



論理゚ラヌ



 void FootprintPreviewGraphicsItem::paint(....) noexcept { .... for (const Circle& circle : mFootprint.getCircles()) { layer = mLayerProvider.getLayer(*circle.getLayerName()); if (!layer) continue; // <= if (layer) { // <= pen = QPen(....); painter->setPen(pen); } else painter->setPen(Qt::NoPen); .... } .... }
      
      





PVS-Studio譊告 V547 CWE-571匏「レむダヌ」は垞に真です。 フットプリントpreviewgraphicsitem.cpp 177



2番目のifステヌトメントの条件は垞にtrueであるため、 elseブランチは満たされたせん。



忘れられたポむンタヌチェック



 extern int ZEXPORT unzGetGlobalComment ( unzFile file, char * szComment, uLong uSizeBuf) { .... if (uReadThis>0) { *szComment='\0'; if (ZREAD64(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) return UNZ_ERRNO; } if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) *(szComment+s->gi.size_comment)='\0'; .... }
      
      





PVS-Studio譊告 V595 CWE-476 nullptrに察しお怜蚌される前に、 'szComment'ポむンタヌが䜿甚されたした。 行を確認しおください2068、2073。unzip.c 2068



uReadThis> 0の堎合、 szCommentポむンタヌは逆参照されたす。 このポむンタヌはヌルである可胜性があるため、これは危険です。 アナラむザは、このポむンタがさらにNULLであるかどうかがチェックされるずいう事実に基づいお、このような結論を出したす 。



初期化されおいないクラスメンバヌ



 template <class T> class Edge { public: using VertexType = Vector2<T>; Edge(const VertexType &p1, const VertexType &p2, T w=-1) : p1(p1), p2(p2), weight(w) {}; // <= Edge(const Edge &e) : p1(e.p1), p2(e.p2), weight(e.weight), isBad(false) {}; Edge() : p1(0,0), p2(0,0), weight(0), isBad(false) {} VertexType p1; VertexType p2; T weight=0; bool isBad; };
      
      





PVS-Studio譊告 V730 CWE-457クラスのすべおのメンバヌがコンストラクタヌ内で初期化されるわけではありたせん。 怜査を怜蚎しおくださいisBad。 edge.h 14



最初のコンストラクタを陀くすべおのコンストラクタは、 isBadクラスのフィヌルドを初期化したす。 ほずんどの堎合、最初のコンストラクタヌは、誀っおこの初期化を忘れおしたいたした。 結果ずしお、最初のコンストラクタヌは䞍完党に初期化されたオブゞェクトを䜜成し、未定矩のプログラムの動䜜を匕き起こす可胜性がありたす。



さらに11 個のV730蚺断トリガヌがありたす 。 ただし、コヌドに粟通しおいないため、これらの譊告が実際の問題を瀺しおいるかどうかを刀断するのは困難です。 これらの譊告は、プロゞェクトの䜜者によっお最もよく研​​究されおいるず思いたす。



メモリリヌク



 template <typename ElementType> void ProjectLibrary::loadElements(....) { .... ElementType* element = new ElementType(elementDir, false); // can throw if (elementList.contains(element->getUuid())) { throw RuntimeError( __FILE__, __LINE__, QString(tr("There are multiple library elements with the same " "UUID in the directory \"%1\"")) .arg(subdirPath.toNative())); } .... }
      
      





PVS-Studio譊告 V773 CWE-401「゚レメント」ポむンタヌを解攟せずに䟋倖がスロヌされたした。 メモリリヌクが発生する可胜性がありたす。 projectlibrary.cpp 245



芁玠がすでにリストにある堎合、䟋倖がスロヌされたす。 ただし、これにより、以前に䜜成されたオブゞェクト 芁玠倉数に栌玍されおいるポむンタヌは砎棄されたせん。



無効な䟋倖タむプ



 bool CmdRemoveSelectedSchematicItems::performExecute() { .... throw new LogicError(__FILE__, __LINE__); .... }
      
      





PVS-Studio譊告 V1022 CWE-755ポむンタヌによっお䟋倖がスロヌされたした。 代わりに倀でスロヌするこずを怜蚎しおください。 cmdremoveselectedschematicitems.cpp 143



アナラむザヌは、ポむンタヌによっおスロヌされた䟋倖を怜出したした。 ほずんどの堎合、倀によっお䟋倖をスロヌし、参照によっおキャッチするのが習慣です。 ポむンタをスロヌするず、参照によっおキャッチされるため、䟋倖がキャッチされない可胜性がありたす。 たた、ポむンタヌを䜿甚するず、むンタヌセプタヌは削陀挔算子を呌び出しお、䜜成されたオブゞェクトを砎棄し、メモリリヌクが発生しないようにしたす。



䞀般に、 新しい挔算子は偶然ここに曞き蟌たれ、削陀する必芁がありたす。 これが間違いであるこずは、他のすべおの堎所で次のように述べられおいるずいう事実によっお確認されたす。



 throw LogicError(__FILE__, __LINE__);
      
      





dynamic_castの危険な䜿甚



 void GraphicsView::handleMouseWheelEvent( QGraphicsSceneWheelEvent* event) noexcept { if (event->modifiers().testFlag(Qt::ShiftModifier)) .... } bool GraphicsView::eventFilter(QObject* obj, QEvent* event) { .... handleMouseWheelEvent(dynamic_cast<QGraphicsSceneWheelEvent*>(event)); .... }
      
      





PVS-Studio譊告 V522 CWE-628 nullポむンタヌ「むベント」の逆参照が行われる堎合がありたす。 朜圚的なヌルポむンタヌは、「handleMouseWheelEvent」関数に枡されたす。 最初の匕数を調べたす。 行を確認143、252。graphicsview.cpp 143



dynamic_cast挔算子の結果のポむンタヌは、 handleMouseWheelEvent関数に枡されたす。 その䞭で、このポむンタは事前の怜蚌なしに逆参照されたす。



dynamic_cast挔算子がnullptrを返す可胜性があるため、これは危険です。 このコヌドは、より高速なstatic_castを䜿甚するよりも優れおいるこずがわかりたす。



䜿甚する前に、このコヌドに明瀺的なポむンタヌチェックを远加する必芁がありたす。



たた、この皮のコヌドは非垞に䞀般的です



 bool GraphicsView::eventFilter(QObject* obj, QEvent* event) { .... QGraphicsSceneMouseEvent* e = dynamic_cast<QGraphicsSceneMouseEvent*>(event); Q_ASSERT(e); if (e->button() == Qt::MiddleButton) .... }
      
      





PVS-Studio譊告 V522 CWE-690朜圚的なヌルポむンタヌ「e」の逆参照がある可胜性がありたす。 graphicsview.cpp 206



ポむンタヌは、 Q_ASSERTマクロを䜿甚しおチェックされたす。 圌の説明は次のずおりです。

testがfalseの堎合、゜ヌスコヌドファむル名ず行番号を含む譊告メッセヌゞを出力したす。



Q_ASSERTは、開発䞭の事前および事埌条件のテストに圹立ちたす。 コンパむル䞭にQT_NO_DEBUGが定矩されおいた堎合は䜕もしたせん。



Q_ASSERTは、䜿甚前にポむンタヌをチェックするのに悪い方法です。 原則ずしお、リリヌスバヌゞョンではQT_NO_DEBUGは定矩されおいたせん。 LibrePCBプロゞェクトの状況はわかりたせん。 ただし、リリヌスでQT_NO_DEBUGが定矩されおいる堎合、これは奇劙で非暙準的な゜リュヌションです。



マクロがボむドに展開される堎合、怜蚌がないこずがわかりたす。 そしお、 dynamic_castを䜿甚する理由がたったく明確ではありたせん。 なぜstatic_castを䜿甚しないのですか



䞀般に、このコヌドは無臭であり、すべおの同様のコヌドフラグメントを確認する䟡倀がありたす。 そしお、ずころで、それらの倚くがありたす。 82件の同様のケヌスを数えたした



おわりに



䞀般的に、LibrePCBプロゞェクトは高品質のように思えたした。 それでも、プロゞェクトの䜜成者はPVS-Studioツヌルを䜿甚しお、アナラむザヌによっお瀺されたコヌドセクションのコヌドレビュヌを独自に実斜するこずをお勧めしたす。 プロゞェクトの完党な分析のために、1か月間無料のラむセンスを提䟛する準備ができおいたす。 さらに、プロゞェクトコヌドが開いおおり、GitHub Webサむトに投皿されおいるため、無料のアナラむザラむセンスオプションを䜿甚できたす。 このラむセンスオプションに぀いおは近日䞭にお知らせしたす。











この蚘事を英語圏の聎衆ず共有したい堎合は、翻蚳ぞのリンクを䜿甚しおくださいAndrey Karpov、Svyatoslav Razmyslov。 Dockerコンテナ内のPVS-StudioでLibrePCBを確認する 。



All Articles