電卓の軌跡をたどる:SpeedCrunch





写真4






計算機コードの研究は続けられています! このレビューでは、SpeedCrunchプロジェクトがレビューされます-無料の計算機の中で2番目に人気があります。



はじめに



SpeedCrunchは、高速のユーザーインターフェイスを備えた高精度で科学的なキーボード駆動の計算機です。 これは、Windows、Linux、およびmacOSで利用可能な無料のオープンソースソフトウェアです。



ソースコードはBitBucketでホストされています 。 アセンブリのドキュメントはあまり好きではありませんでした。私の意見では、これをより詳細に記述する必要があります。 要件には「Qt 5.2以降」が指定されていますが、いくつかの特定のパッケージが必要でしたが、CMakeログから簡単に学ぶことはできませんでした。 ところで、Dockerfileをプロジェクトに適用して、目的の開発者環境をすばやく構成することをお勧めします。



他の計算機と比較するために、Clocユーティリティの出力を表示します。







写真2








他のプロジェクトのバグのレビュー:





PVS-Studioは 、静的解析ツールとして使用されました。 これは、コード品質管理、エラーおよび潜在的な脆弱性の検索のための一連のソリューションです。 サポートされる言語には、C、C ++、C#、およびJavaが含まれます。 アナライザーは、Windows、Linux、およびmacOSで起動できます。



ループ内の奇妙なロジック



V560条件式の一部は常に真です!! RuleFound。 evaluator.cpp 1410



void Evaluator::compile(const Tokens& tokens) { .... while (!syntaxStack.hasError()) { bool ruleFound = false; // <= // Rule for function last argument: id (arg) -> arg. if (!ruleFound && syntaxStack.itemCount() >= 4) { // <= Token par2 = syntaxStack.top(); Token arg = syntaxStack.top(1); Token par1 = syntaxStack.top(2); Token id = syntaxStack.top(3); if (par2.asOperator() == Token::AssociationEnd && arg.isOperand() && par1.asOperator() == Token::AssociationStart && id.isIdentifier()) { ruleFound = true; // <= syntaxStack.reduce(4, MAX_PRECEDENCE); m_codes.append(Opcode(Opcode::Function, argCount)); #ifdef EVALUATOR_DEBUG dbg << "\tRule for function last argument " << argCount << " \n"; #endif argCount = argStack.empty() ? 0 : argStack.pop(); } } .... } .... }
      
      





ruleFound変数に注意してください。 各反復で、falseに設定されます。 しかし、サイクル全体の本体を見ると、特定の条件下でこの変数はtrueに設定されますが、サイクルの新しい反復では考慮されません。 ほとんどの場合、 ruleFound変数はループの前に宣言する必要がありました。



疑わしい比較



V560条件式の一部は常に真です:m_scrollDirection!= 0. resultdisplay.cpp 242



 void ResultDisplay::fullContentScrollEvent() { QScrollBar* bar = verticalScrollBar(); int value = bar->value(); bool shouldStop = (m_scrollDirection == -1 && value <= 0) || (m_scrollDirection == 1 && value >= bar->maximum()); if (shouldStop && m_scrollDirection != 0) { // <= stopActiveScrollingAnimation(); return; } scrollLines(m_scrollDirection * 10); }
      
      





shouldStop変数がtrueの場合、 m_scrollDirection変数の値は-1または1のいずれかになります。したがって、次の条件ステートメントでは、 m_scrollDirection変数の値絶対にゼロでなく、アナライザーは警告します。



V668 「new」演算子を使用してメモリが割り当てられたため、「item」ポインターをnullに対してテストしても意味がありません。 メモリ割り当てエラーの場合、例外が生成されます。 editor.cpp 998



 void EditorCompletion::showCompletion(const QStringList& choices) { .... for (int i = 0; i < choices.count(); ++i) { QStringList pair = choices.at(i).split(':'); QTreeWidgetItem* item = new QTreeWidgetItem(m_popup, pair); if (item && m_editor->layoutDirection() == Qt::RightToLeft) item->setTextAlignment(0, Qt::AlignRight); .... } .... }
      
      





タイプQTreeWidgetItemのオブジェクトのメモリは、new演算子を使用して割り当てられます。 これは、動的メモリを割り当てることができない場合、例外std :: bad_alloc()がスローされることを意味します。 したがって、 アイテムポインターのチェックは不要であり、削除できます。



潜在的なNULLデリファレンス



V595 nullptrに対して検証される前に、「ioparams」ポインターが使用されました。 チェック行:969、983。floatio.c 969



 int cattokens(....) { .... if (printexp) { if (expbase < 2) expbase = ioparams->expbase; // <= .... } dot = '.'; expbegin = "("; expend = ")"; if (ioparams != NULL) // <= { dot = ioparams->dot; expbegin = ioparams->expbegin; expend = ioparams->expend; } .... }
      
      





ioparamsポインター 、有効性がチェックされる前に逆参照されます。 おそらく、潜在的なエラーがコードに忍び込んできました。 間接参照はいくつかの条件下で行われるため、問題はめったに現れませんが、正確に現れます。



ゼロ除算



V609ゼロで除算します。 分母範囲[0..4]。 floatconvert.c 266



 static int lgbase( signed char base) { switch(base) { case 2: return 1; case 8: return 3; case 16: return 4; } return 0; // <= } static void _setlongintdesc( p_ext_seq_desc n, t_longint* l, signed char base) { int lg; n->seq.base = base; lg = lgbase(base); // <= n->seq.digits = (_bitlength(l) + lg - 1) / lg; // <= n->seq.leadingSignDigits = 0; n->seq.trailing0 = _lastnonzerobit(l) / lg; // <= n->seq.param = l; n->getdigit = _getlongintdigit; }
      
      





lgbase関数を使用すると、null値を返すことができ、それによって除算が実行されます。 潜在的に、値2、8、および16以外のものを関数に渡すことができます。



未定義の動作



V610未定義の動作。 シフト演算子「<<」を確認してください。 左のオペランド '(〜0)'は負です。 floatlogic.c 64



 static char _signextend( t_longint* longint) { unsigned mask; signed char sign; sign = _signof(longint); mask = (~0) << SIGNBIT; // <= if (sign < 0) longint->value[MAXIDX] |= mask; else longint->value[MAXIDX] &= ~mask; return sign; }
      
      





ゼロの逆の結果は符号付き型intに格納されるため、結果は負の数になり、その後シフトが実行されます。 負の数を左にシフトすると、未定義の動作になります。



危険な場所の全リスト:





閉じられていないHTMLタグ



V735おそらく正しくないHTML。 「</ body>」終了タグが見つかりましたが、「</ div>」タグが予期されていました。 book.cpp 127



 static QString makeAlgebraLogBaseConversionPage() { return BEGIN INDEX_LINK TITLE(Book::tr("Logarithmic Base Conversion")) FORMULA(y = log(x) / log(a), log<sub>a</sub>x = log(x) / log(a)) END; }
      
      





C / C ++コードでよくあることですが、ソースからは何も明らかではないので、このフラグメントの前処理されたコードを見てみましょう。



写真3









アナライザーは、閉じられていないdivタグを検出しました。 このファイルには多くのhtmlコードのフラグメントがあり、開発者がさらに確認する必要があります。



PVS-Studioを使用して検出された、より不審な場所を次に示します。





代入演算子



V794代入演算子は、「this ==&other」のケースから保護する必要があります。 quantity.cpp 373



 Quantity& Quantity::operator=(const Quantity& other) { m_numericValue = other.m_numericValue; m_dimension = other.m_dimension; m_format = other.m_format; stripUnits(); if(other.hasUnit()) { m_unit = new CNumber(*other.m_unit); m_unitName = other.m_unitName; } cleanDimension(); return *this; }
      
      





ポインターを比較することにより、オブジェクトがそれ自体に割り当てられている状況を考慮することをお勧めします。



つまり、次の2行のコードを関数本体の先頭に追加する必要があります。



 if (this == &other) return *this;
      
      





リマインダー



V601 「false」値は暗黙的に整数型にキャストされます。 cmath.cpp 318



 /** * Returns -1, 0, 1 if n1 is less than, equal to, or more than n2. * Only valid for real numbers, since complex ones are not an ordered field. */ int CNumber::compare(const CNumber& other) const { if (isReal() && other.isReal()) return real.compare(other.real); else return false; // FIXME: Return something better. }
      
      





記事へのコメントでは、不完全なコードに対していくつかの警告が発行されることを示唆している場合があります。 はい、それは起こりますが、実際にそうなると、それについて直接書かれます。



おわりに



3つの計算機の既に利用可能なレビュー:Windows Calculator、Qalculate! とSpeedCrunch。 人気のある電卓のコードを引き続き調査する準備ができています。 ソフトウェアの評価は必ずしも実際の状況を反映するとは限らないため、検証用のプロジェクトを提供できます。



PVS-Studioをダウンロードしてプロジェクトを試して、「計算機」を確認してください:-)











英語を話す聴衆とこの記事を共有したい場合は、翻訳へのリンクを使用してください:Svyatoslav Razmyslov。 電卓の足跡をたどる:SpeedCrunch



All Articles