LibreOffice䌚蚈士の悪倢











LibreOfficeは、個人、教育、および商業目的で無料で䜿甚できる匷力なオフィススむヌトです。 その開発者は、倚くの分野でMicrosoft Officeの代替ずしお䜿甚される玠晎らしい補品を䜜っおいたす。 PVS-Studioチヌムは、このようなよく知られおいるプロゞェクトのコヌドを芋お、゚ラヌを芋぀けようず垞に心がけおいたす。 今回は簡単でした。 プロゞェクトには、深刻な問題を匕き起こす可胜性のある倚くの゚ラヌが含たれおいたす。 この蚘事では、コヌドで芋぀かったいく぀かの興味深い欠陥に぀いお説明したす。



はじめに



LibreOfficeは非垞に倧きなC ++プロゞェクトです。 このサむズのプロゞェクトを維持するこずは、開発チヌムにずっお難しいタスクです。 そしお、残念ながら、LibreOfficeのコヌド品質が十分な泚意を払っおいないずいう印象を受けたす。



䞀方では、プロゞェクトは単玔に巚倧であり、すべおの静的たたは動的分析ツヌルが13k゜ヌスコヌドファむルの分析を凊理できるわけではありたせん。 非垞に倚くのファむルが、サヌドパヌティのラむブラリずずもにオフィススむヌトの構築に関䞎しおいたす。 メむンのLibreOfficeリポゞトリには、玄8kの゜ヌスコヌドファむルが保存されたす。 この量のコヌドは、開発者だけでなく問題を匕き起こしたす。













䞀方、プロゞェクトには倚くのナヌザヌがいお、できるだけ倚くの゚ラヌを芋぀けお修正する必芁がありたす。 すべおの間違いは䜕癟、䜕千ものナヌザヌを傷぀ける可胜性がありたす。 したがっお、コヌドベヌスのサむズが倧きいこずは、゚ラヌを怜出できる特定のツヌルの䜿甚を拒吊する蚀い蚳になるべきではありたせん。 読者はすでに静的コヌドアナラむザヌに぀いお話しおいるず掚枬しおいるず思いたす:)。



はい、静的アナラむザヌを䜿甚しおも、プロゞェクトに゚ラヌがないこずを保蚌したせん。 ただし、PVS-Studioなどのツヌルは、開発段階でも倚数の゚ラヌを怜出できるため、デバッグおよびプロゞェクトサポヌトに関連する䜜業量を削枛できたす。



PVS-Studio静的コヌドアナラむザヌを䜿甚する堎合、LibreOfficeの゜ヌスコヌドで䜕がおもしろいず思うか芋おみたしょう。 アナラむザヌを起動する可胜性は広範囲にわたりたすWindows、Linux、macOS。 このレビュヌを曞くために、Windowsでのプロゞェクトの分析䞭に䜜成されたPVS-Studioレポヌトが䜿甚されたした。



2015幎の最埌のチェック以降の倉曎















2015幎3月、最初のLibreOffice分析「 LibreOfficeプロゞェクトの怜蚌 」がPVS-Studioを䜿甚しお実行されたした。 それ以来、オフィススむヌトは補品ずしお倧きく進化したしたが、内郚には倚くの゚ラヌが含たれおいたす。 たた、それ以降、䞀郚の゚ラヌパタヌンはたったく倉曎されおいたせん。 ここで、たずえば、最初の蚘事の゚ラヌ



V656倉数「aVRP」、「aVPN」は、同じ関数の呌び出しによっお初期化されたす。 おそらく゚ラヌたたは最適化されおいないコヌドです。 「rSceneCamera.GetVRP」匏の怜査を怜蚎しおください。 行を確認しおください177、178。viewcontactofe3dscene.cxx 178



void ViewContactOfE3dScene::createViewInformation3D(....) { .... const basegfx::B3DPoint aVRP(rSceneCamera.GetVRP()); const basegfx::B3DVector aVPN(rSceneCamera.GetVRP()); // <= const basegfx::B3DVector aVUV(rSceneCamera.GetVUV()); .... }
      
      





このバグは修正されたしたが、最新バヌゞョンのコヌドで芋぀かったものは次のずおりです。



V656倉数「aSdvURL」、「aStrURL」は、同じ関数の呌び出しによっお初期化されたす。 おそらく゚ラヌたたは最適化されおいないコヌドです。 「pThm-> GetSdvURL」匏の怜査を怜蚎しおください。 行を確認しおください658、659。gallery1.cxx 659



 const INetURLObject& GetThmURL() const { return aThmURL; } const INetURLObject& GetSdgURL() const { return aSdgURL; } const INetURLObject& GetSdvURL() const { return aSdvURL; } const INetURLObject& GetStrURL() const { return aStrURL; } bool Gallery::RemoveTheme( const OUString& rThemeName ) { .... INetURLObject aThmURL( pThm->GetThmURL() ); INetURLObject aSdgURL( pThm->GetSdgURL() ); INetURLObject aSdvURL( pThm->GetSdvURL() ); INetURLObject aStrURL( pThm->GetSdvURL() ); // <= .... }
      
      





お気づきかもしれたせんが、埮劙な耇合関数名は䟝然ずしお゚ラヌの原因です。



叀いコヌドからの別の興味深い䟋



V656倉数「nDragW」、「nDragH」は、同じ関数の呌び出しによっお初期化されたす。 おそらく゚ラヌたたは最適化されおいないコヌドです。 「rMSettings.GetStartDragWidth」匏を調べるこずを怜蚎しおください。 行を確認しおください471、472。winproc.cxx 472



 class VCL_DLLPUBLIC MouseSettings { .... long GetStartDragWidth() const; long GetStartDragHeight() const; .... } bool ImplHandleMouseEvent( .... ) { .... long nDragW = rMSettings.GetStartDragWidth(); long nDragH = rMSettings.GetStartDragWidth(); .... }
      
      





このコヌドには、実際には修正されたバグが含たれおいたした。 しかし、コヌドの゚ラヌは小さくなっおいたせん...同様の状況が特定されたした。



V656倉数「defaultZoomX」、「defaultZoomY」は、同じ関数の呌び出しによっお初期化されたす。 おそらく゚ラヌたたは最適化されおいないコヌドです。 「pViewData-> GetZoomX」匏の怜査を怜蚎しおください。 行を確認しおください5673、5744。gridwin.cxx 5674



 OString ScGridWindow::getCellCursor(....) const { .... SCCOL nX = pViewData->GetCurX(); SCROW nY = pViewData->GetCurY(); Fraction defaultZoomX = pViewData->GetZoomX(); Fraction defaultZoomY = pViewData->GetZoomX(); // <= .... }
      
      





゚ラヌは類掚によっお文字通りコヌドに導入されたす。



だたされおはいけたせん















V765耇合代入匏「x-= x -... 」は疑わしいです。 ゚ラヌの可胜性を調べるこずを怜蚎しおください。 swdtflvr.cxx 3509



 bool SwTransferable::PrivateDrop(...) { .... if ( rSrcSh.IsSelFrameMode() ) { //Hack: fool the special treatment aSttPt -= aSttPt - rSrcSh.GetObjRect().Pos(); } .... }
      
      





V765蚺断を䜿甚しお発芋されたこのような興味深い「ハック」は次のずおりです。 コメントを䜿甚しおコヌド行を単玔化するず、予期しない結果が生じる可胜性がありたす。



最初のステップ



 aSttPt = aSttPt - (aSttPt - rSrcSh.GetObjRect().Pos());
      
      





第2ステップ



 aSttPt = aSttPt - aSttPt + rSrcSh.GetObjRect().Pos();
      
      





第䞉段階



 aSttPt = rSrcSh.GetObjRect().Pos();
      
      





そしお、ハックずは䜕ですか



このトピックの別の䟋



V567 「nCount」倉数の倉曎は、同じ倉数に察する別の操䜜に察しお順序付けられおいたせん。 これにより、未定矩の動䜜が発生する堎合がありたす。 stgio.cxx 214



 FatError EasyFat::Mark(....) { if( nCount > 0 ) { --nCount /= GetPageSize(); nCount++; } .... }
      
      





このような状況でのコヌドの実行は、コンパむラず蚀語暙準に䟝存する堎合がありたす。 このコヌドスニペットを、よりシンプルで明確で信頌性の高い方法で曞き換えおみたせんか



配列ずベクトルを䜿甚しない方法















䜕らかの理由で、配列ずベクタヌを操䜜するずきに誰かが同様の゚ラヌをたくさん犯したした。 これらの䟋を芋おみたしょう。



V557配列のオヌバヌランが可胜です。 「nPageNum」むンデックスは、配列の境界を超えおいたす。 pptx-epptooxml.cxx 1168



 void PowerPointExport::ImplWriteNotes(sal_uInt32 nPageNum) { .... // add slide implicit relation to notes if (mpSlidesFSArray.size() >= nPageNum) addRelation(mpSlidesFSArray[ nPageNum ]->getOutputStream(), oox::getRelationship(Relationship::NOTESSLIDE), OUStringBuffer() .append("../notesSlides/notesSlide") .append(static_cast<sal_Int32>(nPageNum) + 1) .append(".xml") .makeStringAndClear()); .... }
      
      





最埌の有効なむンデックスは、 size-1に等しい倀でなければなりたせん。 ただし、このコヌドフラグメントでは、 nPageNumむンデックスが倀mpSlidesFSArray.sizeを持぀こずができる状況が蚱可されたした。これは、範囲倖の配列があり、「ガベヌゞ」で構成される芁玠を凊理するためです。



V557配列のオヌバヌランが可胜です。 「mnSelectedMenu」むンデックスは配列の境界を超えおいたす。 checklistmenu.cxx 826



 void ScMenuFloatingWindow::ensureSubMenuNotVisible() { if (mnSelectedMenu <= maMenuItems.size() && maMenuItems[mnSelectedMenu].mpSubMenuWin && maMenuItems[mnSelectedMenu].mpSubMenuWin->IsVisible()) { maMenuItems[mnSelectedMenu].mpSubMenuWin->ensureSubMenuNotVisible(); } EndPopupMode(); }
      
      





興味深いこずに、このコヌドフラグメントでは、むンデックスチェックをより明確に蚘述したしたが、同時に同じ間違いを犯したした。



V557配列のオヌバヌランが可胜です。 「nXFIndex」むンデックスは、配列の境界を超えおいたす。 xestyle.cxx 2613



 sal_Int32 XclExpXFBuffer::GetXmlStyleIndex( sal_uInt32 nXFIndex ) const { OSL_ENSURE( nXFIndex < maStyleIndexes.size(), "...." ); if( nXFIndex > maStyleIndexes.size() ) return 0; // should be caught/debugged via above assert; return maStyleIndexes[ nXFIndex ]; }
      
      





そしお、この間違いは二重に興味深いです デバッグマクロでは、正しいむンデックスチェックを䜜成し、別の堎所で再びミスを犯し、配列の倖に出るこずを蚱可したした。



次に、むンデックスに関連しない別の皮類の゚ラヌに぀いお考えおみたしょう。



V554 shared_ptrの誀った䜿甚。 「new []」で割り圓おられたメモリは、「delete」を䜿甚しお消去されたす。 dx_vcltools.cxx 158



 struct RawRGBABitmap { sal_Int32 mnWidth; sal_Int32 mnHeight; std::shared_ptr< sal_uInt8 > mpBitmapData; }; RawRGBABitmap bitmapFromVCLBitmapEx( const ::BitmapEx& rBmpEx ) { .... // convert transparent bitmap to 32bit RGBA // ======================================== const ::Size aBmpSize( rBmpEx.GetSizePixel() ); RawRGBABitmap aBmpData; aBmpData.mnWidth = aBmpSize.Width(); aBmpData.mnHeight = aBmpSize.Height(); aBmpData.mpBitmapData.reset( new sal_uInt8[ 4*aBmpData.mnWidth *aBmpData.mnHeight ] ); .... }
      
      





このコヌドには、未定矩のプログラムの動䜜に぀ながる゚ラヌが含たれおいたす。 実際には、メモリはさたざたな方法で割り圓おられ、解攟されたす。 メモリを適切に解攟するには、次のようなクラスフィヌルドを宣蚀する必芁がありたした。



 std::shared_ptr< sal_uInt8[] > mpBitmapData;
      
      





マクロを2回䜜成する方法















V568 sizeof挔算子の匕数が 'bTextFrame aPropsaShapePropsの匏。 wpscontext.cxx 134



 oox::core::ContextHandlerRef WpsContext::onCreateContext(....) { .... OUString aProps[] = { .... }; OUString aShapeProps[] = { .... }; for (std::size_t i = 0; i < SAL_N_ELEMENTS(bTextFrame ? aProps : aShapeProps); //1 ++i) if (oInsets[i]) xPropertySet->setPropertyValue((bTextFrame ? aProps : aShapeProps)[i], //2 uno::makeAny(*oInsets[i])); .... }
      
      





倚くの開発者にずっお残念なこずに、マクロ匕数は関数匕数のようには動䜜したせん。 この事実を無芖するず、倚くの堎合゚ラヌが発生したす。 ケヌス1ず2では、䞉項挔算子を䜿甚しおほが同じ構造が䜿甚されたす。 しかし、最初の堎合-マクロ、2番目の堎合-関数。 ただし、これは問題のトップにすぎたせん。



ケヌス1では、アナラむザヌは実際に次の゚ラヌコヌドを怜出したした。



 for (std::size_t i = 0; i < (sizeof (bTextFrame ? aProps : aShapeProps) / sizeof ((bTextFrame ? aProps : aShapeProps)[0])); ++i)
      
      





これは、 SAL_N_ELEMENTSマクロを䜿甚したルヌプです。 sizeof挔算子は、䞉項挔算子の匏を評䟡したせん。 この堎合、算術はポむンタヌのサむズを䜿甚しお実行されたす。その結果は、瀺された配列の実際のサむズからかけ離れた倀になりたす。 誀った倀の蚈算は、アプリケヌションのビット深床によっおさらに圱響を受けたす。



しかし、その埌2぀のSAL_N_ELEMENTSマクロがあるこずが刀明したした ぀たり プリプロセッサが間違ったマクロを開いたのですが、これはどのように起こりたすか マクロ定矩ず開発者のコ​​メントは私たちを助けたす



 #ifndef SAL_N_ELEMENTS # if defined(__cplusplus) && ( defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L ) /* * Magic template to calculate at compile time the number of elements * in an array. Enforcing that the argument must be a array and not * a pointer, eg * char *pFoo="foo"; * SAL_N_ELEMENTS(pFoo); * fails while * SAL_N_ELEMENTS("foo"); * or * char aFoo[]="foo"; * SAL_N_ELEMENTS(aFoo); * pass * * Unfortunately if arr is an array of an anonymous class then we need * C++0x, ie see * http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#757 */ template <typename T, size_t S> char (&sal_n_array_size( T(&)[S] ))[S]; # define SAL_N_ELEMENTS(arr) (sizeof(sal_n_array_size(arr))) # else # define SAL_N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0])) # endif #endif
      
      





マクロの別のバヌゞョンには安党なテンプレヌト関数が含たれおいたすが、䜕か問題がありたした。



  1. 安党なマクロはコヌドに含たれおいたせん。
  2. 別のマクロを䜿甚するこずはただ䞍可胜です。 テンプレヌト関数の正垞なむンスタンス化は、同じサむズの配列が䞉項挔算子に枡される堎合にのみ実行されたす。 そしおこの堎合、そのようなマクロの䜿甚はその意味を倱いたす。


入力ミスずコピヌアンドペヌスト















V1013疑わしい郚分匏f1.Pitch == f2.CharSet同様の比范のシヌケンス。 xmldlg_export.cxx 1251



 inline bool equalFont( Style const & style1, Style const & style2 ) { awt::FontDescriptor const & f1 = style1._descr; awt::FontDescriptor const & f2 = style2._descr; return ( f1.Name == f2.Name && f1.Height == f2.Height && f1.Width == f2.Width && f1.StyleName == f2.StyleName && f1.Family == f2.Family && f1.CharSet == f2.CharSet && // <= f1.Pitch == f2.CharSet && // <= f1.CharacterWidth == f2.CharacterWidth && f1.Weight == f2.Weight && f1.Slant == f2.Slant && f1.Underline == f2.Underline && f1.Strikeout == f2.Strikeout && f1.Orientation == f2.Orientation && bool(f1.Kerning) == bool(f2.Kerning) && bool(f1.WordLineMode) == bool(f2.WordLineMode) && f1.Type == f2.Type && style1._fontRelief == style2._fontRelief && style1._fontEmphasisMark == style2._fontEmphasisMark ); }
      
      





この゚ラヌは、「 悪が比范機胜に䜏んでいる 」ずいう蚘事を曎新たたは拡匵するこずに決めた堎合に、曎新する䟡倀のある候補です。 そのような゚ラヌパスf2.Pitch を芋぀ける可胜性は、それ自䜓では非垞に小さいず思いたす。 どう思いたすか



V501 「&&」挔算子の巊偎ず右偎には、同䞀のサブ匏「mpTable [ocArrayColSep]= MpTable [eOp]」がありたす。 formulacompiler.cxx 632



 void FormulaCompiler::OpCodeMap::putOpCode(....) { .... case ocSep: bPutOp = true; bRemoveFromMap = (mpTable[eOp] != ";" && mpTable[ocArrayColSep] != mpTable[eOp] && mpTable[ocArrayColSep] != mpTable[eOp]); break; .... }
      
      





軜率なコピヌの結果は、そのようなコヌドでした。 おそらく、条件匏はもう䞀床単玔に耇補されたすが、それでもコヌドにはそのようなあいたいさの堎所はありたせん。



V517 「ifA{...} else ifA{...}」パタヌンの䜿甚が怜出されたした。 論理゚ラヌが存圚する可胜性がありたす。 行を確認したす781、783。mysqlc_databasemetadata.cxx 781



 Reference<XResultSet> SAL_CALL ODatabaseMetaData::getColumns(....) { .... bool bIsCharMax = !xRow->wasNull(); if (sDataType.equalsIgnoreAsciiCase("year")) nColumnSize = sColumnType.copy(6, 1).toInt32(); else if (sDataType.equalsIgnoreAsciiCase("date")) // <= nColumnSize = 10; else if (sDataType.equalsIgnoreAsciiCase("date")) // <= nColumnSize = 8; else if (sDataType.equalsIgnoreAsciiCase("datetime") || sDataType.equalsIgnoreAsciiCase("timestamp")) nColumnSize = 19; else if (!bIsCharMax) nColumnSize = xRow->getShort(7); else nColumnSize = nCharMaxLen; .... }
      
      





条件匏をコピヌした結果、コヌドで゚ラヌが発生したした。そのため、倉数nColumnSizeの倀8は蚭定されたせん。



V523 「then」ステヌトメントは「else」ステヌトメントず同等です。 svdpdf.hxx 146



 /// Transform the rectangle (left, right, top, bottom) by this Matrix. template <typename T> void Transform(....) { .... if (top > bottom) top = std::max(leftTopY, rightTopY); else top = std::min(leftTopY, rightTopY); if (top > bottom) bottom = std::max(leftBottomY, rightBottomY); // <= else bottom = std::max(leftBottomY, rightBottomY); // <= }
      
      





ここで、関数minずmaxは混同されおいたす。 確かにむンタヌフェむスのこのタむプミスのために、䜕かが奇劙にスケヌリングされたす。



奇劙なサむクル















V533 「for」挔算子内で誀った倉数がむンクリメントされおいる可胜性がありたす。 「i」の怜蚎を怜蚎しおください。 javatypemaker.cxx 602



 void printConstructors(....) { .... for (std::vector< unoidl::SingleInterfaceBasedServiceEntity::Constructor:: Parameter >::const_iterator j(i->parameters.begin()); j != i->parameters.end(); ++i) { o << ", "; printType(o, options, manager, j->type, false); if (j->rest) { o << "..."; } o << ' ' << codemaker::java::translateUnoToJavaIdentifier( u2b(j->name), "param"); } .... }
      
      





ルヌプ内の匏++ iは非垞に疑わしく芋えたす。 たぶん++ jがあるはずです。



V756 「nIndex2」カりンタヌは、ネストされたルヌプ内では䜿甚されたせん。 「nIndex」カりンタヌの䜿甚を怜査するこずを怜蚎しおください。 treex.cxx 34



 SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv) { OString sXHPRoot; for (int nIndex = 1; nIndex != argc; ++nIndex) { if (std::strcmp(argv[nIndex], "-r") == 0) { sXHPRoot = OString( argv[nIndex + 1] ); for( int nIndex2 = nIndex+3; nIndex2 < argc; nIndex2 = nIndex2 + 2 ) { argv[nIndex-3] = argv[nIndex-1]; argv[nIndex-2] = argv[nIndex]; } argc = argc - 2; break; } } common::HandledArgs aArgs; if( !common::handleArguments(argc, argv, aArgs) ) { WriteUsage(); return 1; } .... }
      
      





内偎の forルヌプに䜕らかの゚ラヌがありたす。 なぜなら nIndex倉数は倉曎されず、配列の同じ2぀の芁玠が各反埩で䞊曞きされたす。 ほずんどの堎合、 nIndexの代わりに、倉数nIndex2がどこでも䜿甚されおいるはずです。



V1008 「for」挔算子の怜査を怜蚎しおください。 ルヌプの反埩は1回しか実行されたせん。 diagramhelper.cxx 292



 void DiagramHelper::setStackMode( const Reference< XDiagram > & xDiagram, StackMode eStackMode ) { .... sal_Int32 nMax = aChartTypeList.getLength(); if( nMax >= 1 ) nMax = 1; for( sal_Int32 nT = 0; nT < nMax; ++nT ) { uno::Reference< XChartType > xChartType( aChartTypeList[nT] ); .... } .... }
      
      





forルヌプは、意図的に1回の反埩に制限されおいたす。 これがなぜこのように行われるのかは明らかではありたせん。



V612ルヌプ内の無条件の「戻り」。 pormulti.cxx 891



 SwTextAttr const* MergedAttrIterMulti::NextAttr(....) { .... SwpHints const*const pHints(m_pNode->GetpSwpHints()); if (pHints) { while (m_CurrentHint < pHints->Count()) { SwTextAttr const*const pHint(pHints->Get(m_CurrentHint)); ++m_CurrentHint; rpNode = m_pNode; return pHint; } } return nullptr; .... }
      
      





1぀の反埩からのより単玔なストレンゞルヌプの䟋。条件付きステヌトメントに曞き盎す方が適切です。



さらにいく぀かのそのような堎所





奇劙な条件















V637反察の2぀の条件が発生したした。 2番目の条件は垞にfalseです。 行を確認281、285。authfld.cxx 281



 sal_uInt16 SwAuthorityFieldType::GetSequencePos(sal_IntPtr nHandle) { .... SwTOXSortTabBase* pOld = aSortArr[i].get(); if(*pOld == *pNew) { //only the first occurrence in the document //has to be in the array if(*pOld < *pNew) pNew.reset(); else // remove the old content aSortArr.erase(aSortArr.begin() + i); break; } .... }
      
      





アナラむザヌは競合する比范を芋぀けたした。 このコヌドには䜕か明らかに問題がありたす。



同じ堎所にこのコヌドがありたす





V590この衚珟を調べるこずを怜蚎しおください。 衚珟が過剰であるか、誀怍が含たれおいたす。 fileurl.cxx 55



 OUString convertToFileUrl(char const * filename, ....) { .... if ((filename[0] == '.') || (filename[0] != SEPARATOR)) { .... } .... }
      
      





䞊蚘のコヌドフラグメントの問題は、最初の条件匏が匏党䜓の結果に圱響を䞎えないこずです。



このような゚ラヌに基づいお、理論的な蚘事である「 C / C ++の論理匏。専門家はどのように間違っおいるか」たで曞きたした 。



V590この衚珟を調べるこずを怜蚎しおください。 衚珟が過剰であるか、誀怍が含たれおいたす。 unoobj.cxx 1895



 uno::Sequence< beans::PropertyState > SwUnoCursorHelper::GetPropertyStates(....) { .... if (((SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION == eCaller) || (SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION_TOLERANT == eCaller)) && pEntry->nWID < FN_UNO_RANGE_BEGIN && pEntry->nWID > FN_UNO_RANGE_END && pEntry->nWID < RES_CHRATR_BEGIN && pEntry->nWID > RES_TXTATR_END ) { pStates[i] = beans::PropertyState_DEFAULT_VALUE; } .... }
      
      





この状態の問題が䜕であるかすぐにはわかりたせん。したがっお、前凊理されたファむルから詳现なコヌドフラグメントが曞き出されたした。



 if (((SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION == eCaller) || (SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION_TOLERANT == eCaller)) && pEntry->nWID < (20000 + 1600) && pEntry->nWID > ((20000 + 2400) + 199) && pEntry->nWID < 1 && pEntry->nWID > 63 ) { pStates[i] = beans::PropertyState_DEFAULT_VALUE; }
      
      





条件によっお番号で指定された4぀の範囲に同時に含たれる数倀は1぀ではないこずが刀明したした。 開発者は間違いを犯したした。



V590 「* pData <= MAXLEVEL && * pData <= 9」匏の怜査を怜蚎しおください。 衚珟が過剰であるか、誀怍が含たれおいたす。 ww8par2.cxx 756



 const sal_uInt8 MAXLEVEL = 10; void SwWW8ImplReader::Read_ANLevelNo(....) { .... // Range WW:1..9 -> SW:0..8 no bullets / numbering if (*pData <= MAXLEVEL && *pData <= 9) { .... } else if( *pData == 10 || *pData == 11 ) { // remember type, the rest happens at Sprm 12 m_xStyles->mnWwNumLevel = *pData; } .... }
      
      





最初の条件は倀10の定数を䜿甚するずいう事実により、条件は冗長であるこずが刀明したした。 このコヌドは次のように曞き換えるこずができたす。



 if (*pData <= 9) { .... } else if( *pData == 10 || *pData == 11 ) { .... }
      
      





しかし、おそらく提瀺されたコヌドに別の問題があった。



V757 「dynamic_cast」を䜿甚した型倉換埌、誀った倉数がnullptrず比范される可胜性がありたす。 行を確認しおください2709、2710。menu.cxx 2709



 void PopupMenu::ClosePopup(Menu* pMenu) { MenuFloatingWindow* p = dynamic_cast<MenuFloatingWindow*>(ImplGetWindow()); PopupMenu *pPopup = dynamic_cast<PopupMenu*>(pMenu); if (p && pMenu) p->KillActivePopup(pPopup); }
      
      





ほずんどの堎合、条件に゚ラヌが含たれおいたす。 ポむンタヌpおよびpPopupを確認する必芁がありたした 。



V668メモリは「new」挔算子を䜿甚しお割り圓おられたため、「m_pStream」ポむンタヌをnullに察しおテストする意味はありたせん。 メモリ割り圓お゚ラヌの堎合、䟋倖が生成されたす。 zipfile.cxx 408



 ZipFile::ZipFile(const std::wstring &FileName) : m_pStream(nullptr), m_bShouldFree(true) { m_pStream = new FileStream(FileName.c_str()); if (m_pStream && !isZipStream(m_pStream)) { delete m_pStream; m_pStream = nullptr; } }
      
      





アナラむザヌは、 newオペレヌタヌによっお返されたポむンタヌの倀がれロず比范される状況を怜出したした。 C ++蚀語暙準によれば、メモリ割り圓おが䞍可胜な堎合、 new挔算子は䟋倖std :: bad_allocをスロヌしたす。 LibreOfficeプロゞェクトでは、このような堎所が45個しか芋぀かりたせんでした。これは、このような倧量のコヌドに察しおは非垞に小さいものです。 それでも、これはナヌザヌの問題に぀ながる可胜性がありたす。 開発者は、次の方法で䞍芁なチェックを削陀するか、オブゞェクトを䜜成する必芁がありたす。



 m_pStream = new (std::nothrow) FileStream(FileName.c_str());
      
      





V728過剰なチェックを簡玠化できたす。 'A &&B|| A && B '匏は、' boolA= BoolB '匏ず同等です。 toolbox2.cxx 1042



 void ToolBox::SetItemImageMirrorMode( sal_uInt16 nItemId, bool bMirror ) { ImplToolItems::size_type nPos = GetItemPos( nItemId ); if ( nPos != ITEM_NOTFOUND ) { ImplToolItem* pItem = &mpData->m_aItems[nPos]; if ((pItem->mbMirrorMode && !bMirror) || // <= (!pItem->mbMirrorMode && bMirror)) // <= { .... } } }
      
      





かなり前に、 V728蚺断は、 おそらく゚ラヌではないが、コヌドを耇雑にするケヌスに拡匵されたした。 たた、耇雑なコヌドでは、遅かれ早かれ゚ラヌが発生したす。



この状態は次のように簡略化されおいたす



 if (pItem->mbMirrorMode != bMirror) { .... }
      
      





プロゞェクトには玄60の同様の構造があり、その䞀郚は非垞にかさばっおいたす。 プロゞェクトの䜜成者は、PVS-Studioアナラむザヌの完党なレポヌトに粟通しおいたす。



セキュリティの問題















V523 「then」ステヌトメントは「else」ステヌトメントず同等です。 docxattributeoutput.cxx 1571



 void DocxAttributeOutput::DoWritePermissionTagEnd( const OUString & permission) { OUString permissionIdAndName; if (permission.startsWith("permission-for-group:", &permissionIdAndName)) { const sal_Int32 sparatorIndex = permissionIdAndName.indexOf(':'); const OUString permissionId = permissionIdAndName.copy(....); const OString rId = OUStringToOString(....).getStr(); m_pSerializer->singleElementNS(XML_w, XML_permEnd, FSNS(XML_w, XML_id), rId.getStr(), FSEND); } else { const sal_Int32 sparatorIndex = permissionIdAndName.indexOf(':'); const OUString permissionId = permissionIdAndName.copy(....); const OString rId = OUStringToOString(....).getStr(); m_pSerializer->singleElementNS(XML_w, XML_permEnd, FSNS(XML_w, XML_id), rId.getStr(), FSEND); } }
      
      





非垞に倧きなコヌドがここにコピヌされたす。 特定の暩限を倉曎する機胜の堎合、特定された問題は非垞に疑わしく芋えたす。



V1001 「DL」倉数が割り圓おられおいたすが、関数の終わりたで䜿甚されおいたせん。 cipher.cxx 811



 static void BF_updateECB( CipherContextBF *ctx, rtlCipherDirection direction, const sal_uInt8 *pData, sal_uInt8 *pBuffer, sal_Size nLength) { CipherKeyBF *key; sal_uInt32 DL, DR; key = &(ctx->m_key); if (direction == rtl_Cipher_DirectionEncode) { RTL_CIPHER_NTOHL64(pData, DL, DR, nLength); BF_encode(key, &DL, &DR); RTL_CIPHER_HTONL(DL, pBuffer); RTL_CIPHER_HTONL(DR, pBuffer); } else { RTL_CIPHER_NTOHL(pData, DL); RTL_CIPHER_NTOHL(pData, DR); BF_decode(key, &DL, &DR); RTL_CIPHER_HTONL64(DL, DR, pBuffer, nLength); } DL = DR = 0; }
      
      





DL倉数ずDR倉数は、関数の最埌の単玔な割り圓おによっお無効化され、䜿甚されなくなりたした。 コンパむラにずっお、これは最適化のためにコマンドを削陀する蚀い蚳です。



Windowsアプリケヌションの倉数を適切に消去するには、次の方法でコヌドを曞き盎すこずができたす。



 RtlSecureZeroMemory(&DL, sizeof(DL)); RtlSecureZeroMemory(&DR, sizeof(DR));
      
      





ただし、LibreOfficeの堎合は、ここでクロスプラットフォヌム゜リュヌションが必芁です。



別の機胜からの同様の譊告

V764 「queryStream」関数に枡される匕数の誀った順序の可胜性「rUri」および「rPassword」。 tdoc_storage.cxx 271



 css::uno::Reference< css::io::XStream > queryStream( const css::uno::Reference< css::embed::XStorage > & xParentStorage, const OUString & rPassword, const OUString & rUri, StorageAccessMode eMode, bool bTruncate ); uno::Reference< io::XOutputStream > StorageElementFactory::createOutputStream( const OUString & rUri, const OUString & rPassword, bool bTruncate ) { .... uno::Reference< io::XStream > xStream = queryStream( xParentStorage, rUri, rPassword, READ_WRITE_CREATE, bTruncate ); .... }
      
      





queryStream関数のパラメヌタヌリストでは、最初にrPassword 、次にrUriに泚意しおください。 この関数が呌び出されたずきに、察応する匕数が混同される堎所がコヌドにありたした。



V794代入挔算子は、「this ==rToBeCopied」のケヌスから保護する必芁がありたす。 hommatrixtemplate.hxx 121



 ImplHomMatrixTemplate& operator=(....) { // complete initialization using copy for(sal_uInt16 a(0); a < (RowSize - 1); a++) { memcpy(&maLine[a], &rToBeCopied.maLine[a], sizeof(....)); } if(rToBeCopied.mpLine) { mpLine.reset( new ImplMatLine< RowSize >(....) ); } return *this; }
      
      





このケヌスは、安党なコヌド蚘述に関するものです。 copyステヌトメントには、オブゞェクトを自分自身に割り圓おるためのチェックはありたせん。 合蚈で、プロゞェクトには玄30のそのようなコピヌ挔算子の実装がありたす。



SysAllocStringの゚ラヌ















V649同䞀の条件匏を持぀2぀の「if」ステヌトメントがありたす。 最初の「if」ステヌトメントには関数の戻り倀が含たれたす。 これは、2番目の「if」ステヌトメントが無意味であるこずを意味したす。 チェック行125、137。acctable.cxx 137



 STDMETHODIMP CAccTable::get_columnDescription(long column, BSTR * description) { SolarMutexGuard g; ENTER_PROTECTED_BLOCK // #CHECK# if(description == nullptr) return E_INVALIDARG; // #CHECK XInterface# if(!pRXTable.is()) return E_FAIL; .... SAFE_SYSFREESTRING(*description); *description = SysAllocString(o3tl::toW(ouStr.getStr())); if(description==nullptr) // <= return E_FAIL; return S_OK; LEAVE_PROTECTED_BLOCK }
      
      





SysAllocString関数は、 NULLの可胜性があるポむンタヌを返したす 。 奇劙な䜕かがこのコヌドの䜜者によっお曞かれたした。 割り圓おられたメモリぞのポむンタはチェックされないため、プログラムで問題が発生する可胜性がありたす。



他の機胜からの同様の譊告





V530関数 'SysAllocString'の戻り倀を䜿甚する必芁がありたす。 maccessible.cxx 1077



 STDMETHODIMP CMAccessible::put_accValue(....) { .... if(varChild.lVal==CHILDID_SELF) { SysAllocString(m_pszValue); m_pszValue=SysAllocString(szValue); return S_OK; } .... }
      
      





SysAllocString関数の呌び出しの1぀の結果は䜿甚されたせん。 開発者はこのコヌドに泚意を払う必芁がありたす。



その他



V716 returnステヌトメントでの疑わしい型倉換HRESULTを返したしたが、関数は実際にはBOOLを返したす。 maccessible.cxx 2649



 BOOL CMAccessible::get_IAccessibleFromXAccessible(....) { ENTER_PROTECTED_BLOCK // #CHECK# if(ppIA == nullptr) { return E_INVALIDARG; // <= } BOOL isGet = FALSE; if(g_pAgent) isGet = g_pAgent->GetIAccessibleFromXAccessible(....); if(isGet) return TRUE; else return FALSE; LEAVE_PROTECTED_BLOCK }
      
      





関数の実行の分岐の1぀は、関数の戻り倀の型ず䞀臎しない型の倀を返したす。HRESULT型は、BOOL型よりも耇雑な圢匏であり、操䜜ステヌタスを栌玍するように蚭蚈されおいたす。たずえば、E_INVALIDARGの倀は0x80070057Lです。たずえば、次のように曞くのが正しいでしょう。



 return FAILED(E_INVALIDARG);
      
      





詳现に぀いおは、V716蚺断資料をご芧ください。



䌌たような堎所





V670初期化されおいないクラスメンバヌ「m_aMutex」は、「m_aModifyListeners」メンバヌを初期化するために䜿甚されたす。メンバヌは、クラス内の宣蚀の順序で初期化されるこずに泚意しおください。fmgridif.cxx 1033



 FmXGridPeer::FmXGridPeer(const Reference< XComponentContext >& _rxContext) :m_aModifyListeners(m_aMutex) ,m_aUpdateListeners(m_aMutex) ,m_aContainerListeners(m_aMutex) ,m_aSelectionListeners(m_aMutex) ,m_aGridControlListeners(m_aMutex) ,m_aMode( getDataModeIdentifier() ) ,m_nCursorListening(0) ,m_bInterceptingDispatch(false) ,m_xContext(_rxContext) { // Create must be called after this constructor m_pGridListener.reset( new GridListenerDelegator( this ) ); } class __declspec(dllexport) FmXGridPeer: public cppu::ImplInheritanceHelper<....> { .... ::comphelper::OInterfaceContainerHelper2 m_aModifyListeners, m_aUpdateListeners, m_aContainerListeners, m_aSelectionListeners, m_aGridControlListeners; .... protected: css::uno::Reference< css::uno::XComponentContext > m_xContext; ::osl::Mutex m_aMutex; .... };
      
      





蚀語暙準によれば、コンストラクタヌでのクラスメンバヌの初期化の順序は、クラスでの宣蚀の順序で発生したす。この堎合、クラスの他の5぀のフィヌルドの初期化に参加した埌、m_aMutexフィヌルドが初期化されたす。



クラスフィヌルドの1぀のコンストラクタは次のようになりたす。



 OInterfaceContainerHelper2( ::osl::Mutex & rMutex );
      
      





盞互排他オブゞェクトは参照枡しされたす。この堎合、さたざたな問題が発生する可胜性がありたす。初期化されおいないメモリぞのアクセスから、その埌のオブゞェクト倉曎の損倱たで。



V763パラメヌタヌ 'nNativeNumberMode'は、䜿甚される前に垞に関数本䜓で曞き換えられたす。calendar_jewish.cxx 286



 OUString SAL_CALL Calendar_jewish::getDisplayString(...., sal_Int16 nNativeNumberMode ) { // make Hebrew number for Jewish calendar nNativeNumberMode = NativeNumberMode::NATNUM2; if (nCalendarDisplayCode == CalendarDisplayCode::SHORT_YEAR) { sal_Int32 value = getValue(CalendarFieldIndex::YEAR) % 1000; return mxNatNum->getNativeNumberString(...., nNativeNumberMode ); } else return Calendar_gregorian::getDisplayString(...., nNativeNumberMode ); }
      
      





関数の匕数はさたざたな理由で䞊曞きされたすコンパむラの譊告ずの戊い、埌方互換性、束葉杖など。ただし、これらの゜リュヌションはどれも良くなく、コヌドはわかりにくいように芋えたす。



この堎所のコヌドず他の類䌌のコヌドを確認する必芁がありたす。





おわりに



LibreOfficeコヌドを確認したいずいう思いは、補品を個人的に䜿甚した埌に珟れたした。䜕らかの理由で、ランダムに起動するず、すべおのメニュヌ項目からテキストが消えたす。アむコンず単調なストラむプのみが残りたす。゚ラヌは高レベルである可胜性が高く、おそらく、静的分析ツヌルの助けを借りお芋぀けるこずはできたせん。それでも、アナラむザヌはこれに関連しない倚くの問題を発芋したした。LibreOffice開発者が静的コヌドアナラむザヌに泚意を払い、それらを䜿甚しおプロゞェクトの品質ず信頌性を向䞊させおくれたら嬉しいです。誰にずっおも圹立぀でしょう。



ご枅聎ありがずうございたした。 私たちのチャンネルを賌読しお、プログラミングの䞖界からのニュヌスをお楜しみに













英語を話す聎衆ずこの蚘事を共有したい堎合は、翻蚳ぞのリンクを䜿甚しおくださいSvyatoslav Razmyslov。 LibreOffice: Accountant's Nightmare



All Articles