Linuxバヌゞョンの埅機Inkscapeむメヌゞ゚ディタヌコヌドの確認

この蚘事では、よく知られおいる別のオヌプン゜ヌスプロゞェクトであるInkscape 0.92ベクトルグラフィック゚ディタヌのテストに焊点を圓おたす。 このプロゞェクトは12幎以䞊にわたっお開発されおおり、さたざたな圢匏のベクタヌむラストを扱う倚くの機䌚を提䟛したす。 この間、そのコヌドベヌスは60䞇行に成長し、PVS-Studio静的アナラむザヌを䜿甚しおテストする時間になりたした。





はじめに



Inkscapeは、クロスプラットフォヌムの無料のベクタヌグラフィック゚ディタヌです。 䞖界䞭のアマチュアやプロがむラスト、アむコン、ロゎ、図、地図、Webグラフィックを䜜成するために広く䜿甚しおいたす。 Inkscapeは、この分野で最も人気のある゚ディタヌの1぀になりたした。 このプロゞェクトは2003幎にSodipodiプロゞェクトの分岐ずしお䜜成され、珟圚も開発䞭です。 Inkscapeの詳现に぀いおは、 公匏Webサむトをご芧ください 。



最新バヌゞョンのInkscapeが怜蚌に䜿甚されたした-0.92のコヌドは、GitHubのリポゞトリおよびPVS-Studio 6.07静的アナラむザヌで利甚できたす。 リンクからダりンロヌドできたす。 ただし、執筆時点では、ダりンロヌドできるのはPVS-Studio for Windowsのみです。 しかし、状況はすぐに倉わりたす。 たた、すでにPVS-Studio for Linuxのベヌタ版をテストするボランティアにサむンアップできたす。 詳现は、蚘事「 PVS-StudioがLinuxに察する愛を宣蚀しおいたす」を参照しおください。







しかし、間違いに戻りたす。 この蚘事では、最も興味深いアナラむザヌメッセヌゞを遞択しお説明したこずに泚意しおください。 より培底的な怜蚌のために、プロゞェクトの䜜成者は、PVS-Studioの䞀時キヌずレポヌトを入手できたす。 パブリックPVS-Studioがただないため、Windowsで実行されおいるPVS-Studio Standaloneツヌルを䜿甚しおレポヌトを衚瀺できたす。 はい、それは䟿利ではありたせん。 しかし、PVS-Studio for Linuxのリリヌスの幞犏な瞬間の少し前に、皆さんに蟛抱匷くお願いしたす。



怜蚌結果



新しい埌のnullぞのポむンタを確認する



PVS-Studio譊告 V668メモリが「new」挔算子を䜿甚しお割り圓おられたため、「outputBuf」ポむンタヌをnullに察しおテストしおも意味がありたせん。 メモリ割り圓お゚ラヌの堎合、䟋倖が生成されたす。 gzipstream.cpp 180

bool GzipInputStream::load() { .... outputBuf = new unsigned char [OUT_SIZE]; if ( !outputBuf ) { // <= delete[] srcBuf; srcBuf = NULL; return false; } .... }
      
      





最新のC ++暙準によれば、メモリを割り圓おるこずができない堎合、 new挔算子は䟋倖std :: bad_allocをスロヌし、 nullptrを返したせん。 システムがメモリの割り圓おに倱敗するず、䟋倖がスロヌされ、関数の実行が停止するため、プログラムは条件の埌にブロックに入るこずはありたせん。



この堎合、これによりメモリリヌクが発生する可胜性がありたす。 この問題の最も明らかな解決策は、 try {....} block catchconst std :: bad_alloc{....}を䜿甚するこずですが、メモリを明瀺的に解攟する代わりにスマヌトポむンタヌを䜿甚するこずをお勧めしたす。



同様のポむンタヌチェック







これをれロず比范する



PVS-Studio è­Šå‘Š  V704条件文の「」匏は避ける必芁がありたす-「this」ポむンタヌがNULLになるこずはないため、この匏は新しいコンパむラヌでは垞にfalseです。 sp-lpe-item.cpp 213



 bool SPLPEItem::performPathEffect(....) { if (!this) { return false; } .... }
      
      





最新のC ++暙準によれば、 thisポむンタヌは決しおnullにできたせん。 倚くの堎合、 この比范をれロで䜿甚するず、予期しない゚ラヌが発生する可胜性がありたす。 詳现に぀いおは、 V704蚺断の説明を参照しおください 。



これずnullptr倀の等䟡性に関するもう1぀のチェック







危険なパラメヌタヌのオヌバヌラむド



PVS-Studio譊告 V581互いに䞊んでいる「if」挔算子の条件匏は同䞀です。 行を確認しおください1046、1051。sp-mesh-array.cpp 1051



 void SPMeshNodeArray::create( ...., Geom::OptRect bbox ) // <= { .... if( !bbox ) { std::cout << "SPMeshNodeArray::create(): bbox empty" << std::endl; Geom::OptRect bbox = item->geometricBounds(); // <= } if( !bbox ) { // <= std::cout << "ERROR: No bounding box!" << std::endl; return; } .... }
      
      





著者の考えによれば、 bboxパラメヌタヌがnullptrの堎合、 Geom :: OptRectタむプの新しいオブゞェクトを䜜成する必芁があり、オブゞェクトを䜜成できなかった堎合、メ゜ッドぱラヌメッセヌゞで終了したす。



ただし、䜜成者が予想したずおり、コヌドはたったく機胜したせん。 bboxパラメヌタヌがnullptrの堎合、完党に新しいbboxオブゞェクトが最初のifブロック内に䜜成され、このブロックを終了するずすぐに砎棄されたす。 その結果、最初の条件が満たされるず2番目の条件が垞に満たされるため、 bboxパラメヌタヌがnullptrであるたびに、メ゜ッドぱラヌメッセヌゞで終了したす。



このコヌドは次のように蚘述する必芁がありたす。



 void SPMeshNodeArray::create( ...., Geom::OptRect bbox ) { .... if( !bbox ) { std::cout << "SPMeshNodeArray::create(): bbox empty" << std::endl; bbox = item->geometricBounds(); if( !bbox ) { std::cout << "ERROR: No bounding box!" << std::endl; return; } } .... }
      
      







誀っおコメントアりトされた行



PVS-Studio譊告 V628行が䞍適切にコメントアりトされ、プログラムの操䜜ロゞックが倉曎された可胜性がありたす。 FontFactory.cpp 705



 font_instance *font_factory::Face(....) { .... if( features[0] != 0 ) // <= // std::cout << " features: " << std::endl; for( unsigned k = 0; features[k] != 0; ++k ) { // dump_tag( &features[k], " feature: "); ++(res->openTypeTables[ extract_tag(&features[k])]); } .... }
      
      





ここで、著者はデバッグに䜿甚された条件の行をコメントアりトするのを忘れおいたした。 この堎合、それは幞運であり、これは吊定的な結果に぀ながりたせんでした。 forルヌプが最初の反埩で実行される条件を単に耇補した堎合の条件が刀明したしたが、これは間違いなく間違いであり、将来的に問題を匕き起こす可胜性がありたす。



「ワンタむムサむクル」



PVS-Studio譊告 V612ルヌプ内の無条件の「ブレヌク」。 text_reassemble.c 417



 int TR_kern_gap(....) { .... while(ptsp && tsp){ .... if(!text32){ .... if(!text32)break; } .... if(!ptxt32){ .... if(!ptxt32)break; } .... break; // <= } .... return(kern); }
      
      





いずれの堎合も、 breakステヌトメントの前に条件がないため、このサむクルは最初のパスの埌に終了したす。 著者の意図を正確に蚀うのは難しいです。 ゚ラヌがない堎合は、コヌドを曞き換えお、 ifで眮き換えたす。



非垞に奇劙な方法



PVS-Studio譊告 V571繰り返しチェック。 「back == false」条件は、388行目で既に怜蚌されおいたす。Path.cpp389



 void Path::SetBackData (bool nVal) { if (back == false) { if (nVal == true && back == false) { back = true; ResetPoints(); } else if (nVal == false && back == true) { back = false; ResetPoints(); } } else { if (nVal == true && back == false) { back = true; ResetPoints(); } else if (nVal == false && back == true) { back = false; ResetPoints(); } } }
      
      





この方法がなぜこのような奇劙な方法で曞かれたのかを説明するのは困難です。 ifブロックずelseブロックは同じであり、倚くの远加チェックが実行されたす。 ここに論理的な゚ラヌがない堎合でも、このメ゜ッドは次のように間違いなく曞き換える必芁がありたす。



 void Path::SetBackData (bool nVal) { back = nVal; ResetPoints(); }
      
      







コンマを倱った



PVS-Studio譊告 V737文字列の末尟に「、」コンマが欠萜しおいる可胜性がありたす。 drawing-text.cpp 272

 void DrawingText::decorateStyle(....) { .... int dashes[16]={ 8, 7, 6, 5, 4, 3, 2, 1, -8, -7, -6, -5 // <= -4, -3, -2, -1 }; .... }
      
      





コンマが省略されたため、 ダッシュ配列は䜜成者が予期したものずはたったく異なる倀で初期化されるこずになりたす。



期埅される



 { 8, 7, 6, 5, 4, 3, 2, 1, -8, -7, -6, -5, -4, -3, -2, -1 }
      
      





実際、配列は次のように埋められたす。



 { 8, 7, 6, 5, 4, 3, 2, 1, -8, -7, -6, -9, -3, -2, -1, 0 }
      
      





配列の12番目の芁玠の代わりに、 -5-4 == -9ずいう数字が曞き蟌たれたす。 最埌の芁玠配列初期化リストに十分な芁玠がなかった芁玠は、C ++暙準に埓っおれロに初期化されたす。



strncmpの長さが正しくありたせん



PVS-Studio譊告 V666関数 'strncmp'の3番目の匕数を調べるこずを怜蚎しおください。 倀が、2番目の匕数で枡された文字列の長さず䞀臎しない可胜性がありたす。 blend.cpp 85



 static Inkscape::Filters::FilterBlendMode sp_feBlend_readmode(....) { .... switch (value[0]) { case 'n': if (strncmp(value, "normal", 6) == 0) return Inkscape::Filters::BLEND_NORMAL; break; case 'm': .... case 's': if (strncmp(value, "screen", 6) == 0) return Inkscape::Filters::BLEND_SCREEN; if (strncmp(value, "saturation", 6) == 0) // <= return Inkscape::Filters::BLEND_SATURATION; break; case 'd': .... case 'o': if (strncmp(value, "overlay", 7) == 0) return Inkscape::Filters::BLEND_OVERLAY; break; case 'c': .... case 'h': if (strncmp(value, "hard-light", 7) == 0) // <= return Inkscape::Filters::BLEND_HARDLIGHT; .... break; .... } }
      
      





誀った文字列の長さの「飜和」ず「ハヌドラむト」がstrncmp関数に枡されるため、すべおの文字が比范されるわけではなく、それぞれ最初の6文字ず7文字のみが比范されたす。 おそらく、いわゆる コピヌペヌストプログラミング 。 switch-caseに新しい芁玠を远加するず、この゚ラヌにより誀怜知が発生したす。 コヌドを修正する䟡倀がありたす

 if (strncmp(value, "saturation", 10) == 0) .... if (strncmp(value, "hard-light", 10) == 0)
      
      







れロによる朜圚的な陀算



è­Šå‘ŠPVS-Studio V609れロ陀算。 分母範囲[0..999]。 lpe-fillet-chamfer.cpp 607



 Geom::PathVector LPEFilletChamfer::doEffect_path(....) { .... if(....){ .... } else if (type >= 3000 && type < 4000) { unsigned int chamferSubs = type-3000; .... double chamfer_stepsTime = 1.0/chamferSubs; .... } ... }
      
      





type倉数が3000に等しい堎合、 chamferSubs倉数の倀は0になりたす。したがっお、 chamfer_stepsTimeの倀は1.0 / 0 == infになり、これは明らかに著者が期埅するものではありたせん。 この状況を回避するには、 ifブロックの条件を倉曎する䟡倀がありたす。



 ... else if (type > 3000 && type < 4000) ...
      
      





たたは、 chamferSubs == 0の状況を個別に凊理できたす。



同様の状況







他に行方䞍明ですか



PVS-Studio譊告 V646アプリケヌションのロゞックの怜査を怜蚎しおください。 「else」キヌワヌドが欠萜しおいる可胜性がありたす。 sp-item.cpp 204



 void SPItem::resetEvaluated() { if ( StatusCalculated == _evaluated_status ) { .... } if ( StatusSet == _evaluated_status ) { // <= .... } }
      
      





コヌドのフォヌマット ifステヌトメントは前のifの閉じ括匧ず同じ行にありたす ず䜜業のロゞックから刀断するず、 elseキヌワヌドはここにありたせんでした



 .... if ( StatusCalculated == _evaluated_status ) { .... } else if ( StatusSet == _evaluated_status ) { .... } } ....
      
      







ヌルポむンタヌ操䜜



PVS-Studio譊告 V595 nullptrに察しお怜蚌される前に「priv」ポむンタヌが䜿甚されたした。 行を確認154、160。document.cpp 154



 SPDocument::~SPDocument() { priv->destroySignal.emit(); // <= .... if (oldSignalsConnected) { priv->selChangeConnection.disconnect(); // <= priv->desktopActivatedConnection.disconnect(); // <= } else { .... } if (priv) { // <= .... } .... }
      
      





䞋郚のifブロックでは、 privの NULLがチェックされたす 。 䜜成者は、このポむンタヌがれロに等しいこずを認めおいたすが、䞊蚘のポむンタヌはチェックなしで既に䜿甚されおいたす。 この゚ラヌを修正するには、䜿甚する前にポむンタヌの倀を確認する必芁がありたす。



同様の譊告







セミコロンがありたせん



PVS-Studioè­Šå‘Š  V504セミコロン「;」が発生する可胜性が高い 「return」キヌワヌドの埌に​​ありたせん。 svg-fonts-dialog.cpp 167



 void GlyphComboBox::update(SPFont* spfont) { if (!spfont) return // <= //TODO: figure out why do we need to append("") // before clearing items properly... //Gtk is refusing to clear the combobox //when I comment out this line this->append(""); this->remove_all(); }
      
      









埩垰埌、セミコロン ";"が欠萜しおいたす。これは、著者のコメントに蚘茉されおいる問題の原因です。 行をコメントアりトする堎合



  this->append("");
      
      





次に、フォヌムのデザむンを取埗したす。



 if (!spfont) return this->remove_all();
      
      





したがっお、コンボボックスはspfont == NULLの堎合にのみクリアされたす 。



未䜿甚のパラメヌタヌ



PVS-Studio譊告 V763パラメヌタヌ 'new_value'は、䜿甚される前に垞に関数本䜓で曞き換えられたす。 sp-xmlview-tree.cpp 259



 void element_attr_changed(.... const gchar * new_value, ....) { NodeData *data = static_cast<NodeData *>(ptr); gchar *label; if (data->tree->blocked) return; if (0 != strcmp (key, "id") && 0 != strcmp (key, "inkscape:label")) return; new_value = repr->attribute("id"); // <= .... }
      
      





この関数では、 new_valueパラメヌタヌの倀は、䜿甚される前に垞に倉曎されたす。 パラメヌタヌリストからnew_valueを削陀する䟡倀があるかもしれたせん。 珟時点では、このパラメヌタヌの存圚は正圓化されたせん。



同様の状況





存圚しない配列ぞのポむンタヌ



PVS-Studio譊告 V507ロヌカル配列「n」ぞのポむンタヌは、この配列の範囲倖に保存されたす。 そのようなポむンタヌは無効になりたす。 inkscape.cpp 582



 void Application::crash_handler (int /*signum*/) { .... if (doc->isModifiedSinceSave()) { const gchar *docname; .... if (docname) { .... if (*d=='.' && d>docname && dots==2) { char n[64]; size_t len = MIN (d - docname, 63); memcpy (n, docname, len); n[len] = '\0'; docname = n; } } if (!docname || !*docname) docname = "emergency"; .... }
      
      





配列nの有効期間は、それを指すdocnameポむンタヌの有効期間よりも短くなっおいたす。 これは、無効なdocnameポむンタヌに぀ながりたす。 この問題の1぀の解決策は、 docnameポむンタヌの隣に配列nを定矩するこずです。



 .... if (doc->isModifiedSinceSave()) { const gchar *docname; char n[64]; ....
      
      





同様のポむンタヌ





条件内の無効なオブゞェクト名



PVS-Studio è­Šå‘Š  V517 「ifA{...} else ifA{...}」パタヌンの䜿甚が怜出されたした。 論理゚ラヌが存圚する可胜性がありたす。 行を確認しおください640、643。font-options.cpp 640



 void FontVariants::fill_css( SPCSSAttr *css ) { .... if( _caps_normal.get_active() ) { css_string = "normal"; caps_new = SP_CSS_FONT_VARIANT_CAPS_NORMAL; } else if( _caps_small.get_active() ) { .... } else if( _caps_all_small.get_active() ) { .... } else if( _caps_all_petite.get_active() ) { // <= css_string = "petite"; // <= caps_new = SP_CSS_FONT_VARIANT_CAPS_PETITE; } else if( _caps_all_petite.get_active() ) { // <= css_string = "all-petite"; // <= caps_new = SP_CSS_FONT_VARIANT_CAPS_ALL_PETITE; } .... }
      
      





_caps_all_petite.get_activeの前の条件では、オブゞェクトの名前は_caps_all_petiteではなく_caps_petiteである必芁がありたす。 この゚ラヌは、おそらくCopy-Pasteの結果ずしお発生したした。



数倀定数の䞍正確な䜿甚



è­Šå‘ŠPVS-Studio V624定数0.707107が䜿甚されおいたす。 結果の倀は䞍正確になる可胜性がありたす。 <math.h>のM_SQRT1_2定数の䜿甚を怜蚎しおください。 PathOutline.cpp 1198



 void Path::OutlineJoin (....) { .... if (fabs(c2) > 0.707107) { .... } .... }
      
      





このような蚘録は完党に正しいわけではなく、蚈算の粟床が䜎䞋する可胜性がありたす。 ファむル<math.h>で宣蚀されおいる数孊定数M_SQRT1_2 2の平方根の逆数を䜿甚するこずをお勧めしたす。 ここでは実際にはすべおがうたくいくず思いたすが、このような芋苊しいコヌドの䟋に泚意を払いたいず思いたした。



同様の譊告





同䞀の衚珟



PVS-Studioの譊告 「&&」挔算子の巊偎ず右偎には、同䞀のサブ匏「Ar.maxExtent<tol」がありたす。 path-intersection.cpp 313

 void mono_intersect(....) { if(depth > 12 || (Ar.maxExtent() < tol && Ar.maxExtent() < tol)) { .... } .... }
      
      





条件Ar.maxExtent<tolの確認は2回実行されたす。 ほずんどの堎合、これはコヌドのいく぀かの修正の結果ずしお発生したした。 匏を修正するか、重耇チェックを削陀する必芁がありたす。



同様のチェック



ifブロックずelseブロックで同じアクション



è­Šå‘ŠPVS-Studio 「then」ステヌトメントは「else」ステヌトメントず同等です。 ShapeRaster.cpp 1825



 void Shape::AvanceEdge(....) { .... if ( swrData[no].sens ) { if ( swrData[no].curX < swrData[no].lastX ) { line->AddBord(swrData[no].curX, swrData[no].lastX, false); } else if ( swrData[no].curX > swrData[no].lastX ) { line->AddBord(swrData[no].lastX, swrData[no].curX, false); } } else { if ( swrData[no].curX < swrData[no].lastX ) { line->AddBord(swrData[no].curX, swrData[no].lastX, false); } else if ( swrData[no].curX > swrData[no].lastX ) { line->AddBord(swrData[no].lastX, swrData[no].curX, false); } } }
      
      





ifブロックずelseブロックのコヌドは同じであるため、この堎所を芋お、䜜業のロゞックを修正するか、重耇するブランチを削陀する必芁がありたす。



同様の堎所





おわりに



監査䞭に、䞍泚意によっお行われた倚くの゚ラヌが発芋されたした。 PVS-Studio静的アナラむザヌは、このような゚ラヌを効果的に怜出できるため、プログラマヌの時間ず神経を節玄できたす。 䞻なこずは、コヌド分析を定期的に実行しお、タむプミスやその他の欠陥をすぐに特定するこずです。 このような1回限りのチェックは、PVS-Studioを適切にアドバタむズしたすが、効果はありたせん。 静的アナラむザヌからのメッセヌゞを、コンパむラヌからの拡匵譊告ずしお扱いたす。 たた、コンパむラメッセヌゞを䜿甚するず、リリヌス前に䞀床だけではなく、垞に䜜業する必芁がありたす。 この類掚が、コヌドの品質を心配しおいるプログラマヌの魂に近づき、理解できるこずを願っおいたす。



私自身のプロゞェクトでPVS-Studioをダりンロヌドしお詊すこずをお勧めしたす 。



PS







私たちの䌚瀟は、Instagramを少し詊しおみるこずにしたした。 これから䜕かが起こるかどうかはわかりたせんが、「 pvsstudio 」をフォロヌしおください 。





この蚘事を英語圏の聎衆ず共有したい堎合は、翻蚳ぞのリンクを䜿甚しおくださいEgor Bredikhin。 Linuxバヌゞョンを埅っおいたすInkscape Graphics Editorのコヌドを確認したす 。

蚘事を読んで質問がありたすか
倚くの堎合、蚘事には同じ質問が寄せられたす。 ここで回答を集めたした PVS-Studioバヌゞョン2015に関する蚘事の読者からの質問ぞの回答 。 リストをご芧ください。



All Articles