最近、次のプロジェクトをチェックすることについて話しましたが、これは非常に高品質のコードであり、エラーがほとんどないことを繰り返しました。 例は、 Apache 、 MySQL 、 Chromiumなどのプロジェクトの分析です。 このようなアプリケーションを選択する理由は明らかだと思います。 誰もが彼らについて知っていて、学生Vasyaの論文で見つけられる恐怖に興味がある人はいません。 しかし、たまに偶然手に入れたプロジェクトを信じていることもあります。 これらのプロジェクトのいくつかは、私の繊細で脆弱な魂に大きな印象を残します。 今回は、Intel®Energy Checker SDK(IEC SDK)を確認しました。
Intel Energy Checker SDKは、わずか74500行のコードを含む小さなCプロジェクトです。 比較のために、WinMergeプロジェクトのサイズは186,000行のコードであり、プラグインを含むMiranda IMプロジェクトのサイズは約950,000行のコードです。
ところで、この記事の冒頭にいる間に、このプロジェクトにいくつの「 goto 」演算子が含まれるかを推測してみてください。 数字を作った? その場合は、続行します。
一般に、これは一般に知られていない小さなプロジェクトの1つです。 このようなプロジェクトのコードを調べると、次のような類似性が生じます。 有名な良い通りを長い間歩くことができ、水たまりはほとんど見えません。 しかし、水たまりはすぐに見つかるだけでなく、足を濡らさずに通り抜けることができるかどうかは明らかではないので、通りを切って中庭を覗く価値があります。
IEC SDKは、「 govnokod 」という言葉が頭に浮かぶテキストを考慮すると、プロジェクトに起因する可能性があります。 コードがひどいとは言えませんが、もっとひどくなります。 しかし、自分で見てください。 プロジェクト全体で機能するのは247のみです。 少し言ってくれますか? もちろん、十分ではありません。 しかし、それらのいくつかのサイズは衝撃的です。 通常、4つの関数のサイズは2000行を超えます。
V553 「pl_open」関数の本体の長さが2000行を超えています。 コードのリファクタリングを検討する必要があります。 pl_csv_logger producer_link.c 379
V553「pl_attach」関数の本体の長さが2000行を超えています。 コードのリファクタリングを検討する必要があります。 pl_csv_logger producer_link.c 9434
V553「メイン」機能の本体の長さが2000行を超えています。 コードのリファクタリングを検討する必要があります。 cluster_energy_efficiency cee.c 97
V553「メイン」機能の本体の長さが2000行を超えています。 コードのリファクタリングを検討する必要があります。 pl2ganglia pl2ganglia.c 105
示しており、ここにディレクトリ名の長さを取得する方法があります:
#define PL_FOLDER_STRING "C:\\productivity_link" #define PL_PATH_SEPARATOR_STRING "\\" #define PL_APPLICATION_NAME_SEPARATOR_STRING "_" ... pl_root_name_length = strlen(PL_FOLDER_STRING); pl_root_name_length += strlen(PL_PATH_SEPARATOR_STRING); pl_root_name_length += application_name_length; pl_root_name_length += strlen(PL_APPLICATION_NAME_SEPARATOR_STRING); pl_root_name_length += PL_UUID_MAX_CHARS; pl_root_name_length += strlen(PL_PATH_SEPARATOR_STRING);
この場所は速度にとって重要ではなく、行の長さの計算を最適化する意味がないことを理解しています。 しかし、芸術を愛するために、#define STRLEN(s)(sizeof(s)/ sizeof(* s)-1)のようなマクロを作成できます。 さらに、私の美意識は、「C:\\」を含む行が表示されるという事実に苦しんでいます。 私はそのようなマクロに驚いています:
#define PL_INI_WINDOWS_FOLDER "C:\\productivity_link" #define PL_INI_WINDOWS_LC_FOLDER "c:\\productivity_link" #define PLH_FOLDER_SEARCH _T("C:\\productivity_link\\*")
ただし、このコードは必要なことを行うので、プログラミングスタイルには焦点を合わせません。 PVS-Studioがこのような小さなサイズのプロジェクトで発見したエラーの数は、はるかに恐ろしいものです。 74,000行からは程遠いコードがチェックされたことに留意する必要があります。 コードの約3分の1はLINUX / SOLARIS / MACOSX用であり、テストされていない#ifdef /#endifコードブランチにあります。 #ifdef /#endifからの不可解なフォレストは一般に別のトピックですが、コードデザインについてはもう話さないことを約束しました。 希望する人は、 ソースコードを自分で調べることができます。
IEC SDKコードには、低レベルでアレイを操作することによって発生する可能性がある異種のエラーが含まれています。 ただし、この種のエラーはC言語の非常に典型的なものです。
配列の外部のメモリにアクセスするコードがあります:
V557配列のオーバーランが可能です。 '255'インデックスは配列の境界を超えています。 pl2ganglia pl2ganglia.c 1114
#define PL_MAX_PATH 255 #define PL2GANFLIA_COUNTER_MAX_LENGTH PL_MAX_PATH char name[PL_MAX_PATH]; int main(int argc, char *argv[]) { ... p->pl_counters_data[i].name[ PL2GANFLIA_COUNTER_MAX_LENGTH ] = '\0'; ... }
配列の外側に端末ゼロを書き込む際の古典的な欠陥を扱っています。 する必要があります:
p->pl_counters_data[i].name[ PL2GANFLIA_COUNTER_MAX_LENGTH - 1 ] = '\0';
ゼロ構造のエラーがあります。
V568 sizeof()演算子の引数が '&file_data'式であることは奇妙です。 pl_csv_logger producer_link_helper.c 1667
V568 sizeof()演算子の引数が '&file_data'式であることは奇妙です。 pl_csv_logger producer_link_helper.c 1831
V512 「memset」関数を呼び出すと、バッファー「pconfig」のアンダーフローが発生します。 pl_csv_logger producer_link_helper.c 1806
このような誤ったゼロ化の例:
int plh_read_pl_folder(PPLH_PL_FOLDER_INFO pconfig) { ... WIN32_FIND_DATA file_data; ... memset( &file_data, 0, sizeof(&file_data) ); ... }
内部にゴミがあるWIN32_FIND_DATA構造を使用する場合、ファイルの検索はうまくいきません。 ただし、このプログラミングアートのWindowsバージョンに興味がある人はほとんどいないという疑いがあります。 たとえば、コードは「Unicode文字セットを使用」モードでコンパイルされますが、これは最後まで設計されていません。 どうやら、誰も考えなかった。 Visual Studio用のプロジェクトを作成しました。デフォルトでは、「文字セット」設定はUNICODEの使用を設定するだけです。
その結果、線が部分的にしかクリアされない場所がc個あります。 このようなコードは次のとおりです。
V512 「memset」関数を呼び出すと、バッファー「(pl_cvt_buffer)」のアンダーフローが発生します。 pl_csv_logger producer_link_helper.c 683
#define PL_MAX_PATH 255 typedef WCHAR TCHAR, *PTCHAR; TCHAR pl_cvt_buffer[PL_MAX_PATH] = { '\0' }; int plh_read_pl_config_ini_file(...) { ... ZeroMemory( pl_cvt_buffer, PL_MAX_PATH ); ... }
ただし、UNICODEを無効にしても役に立たない場所があります。 ここでは、テキストの代わりに、理解できないものが印刷されます。
V576形式が正しくありません 。 'wprintf'関数の2番目の実引数を確認することを検討してください。 wchar_t型シンボルの文字列へのポインターが必要です。 プロデューサーproducer.c 166
int main(void) { ... char *p = NULL; ... #ifdef __PL_WINDOWS__ wprintf( _T("Using power link directory: %s\n"), p ); #endif // __PL_WINDOWS__ ... }
説明します。 wprintf関数は、「wchar_t *」タイプの文字列を予期し、「char *」タイプの文字列が渡されます。
このような他のエラー、および小さな間違いがあります:
V571定期的なチェック。 「if(ret == PL_FAILURE)」条件は1008行目で既に検証されています。pl_csv_logger pl_csv_logger.c 1009
if(ret == PL_FAILURE) { if(ret == PL_FAILURE) { pl_csv_logger_error( PL_CSV_LOGGER_ERROR_UNABLE_TO_READ_PL );
気付いたすべての欠陥をリストしようとしても、そのポイントはわかりません。 誰かが望めば、プロジェクトをチェックしてメッセージを分析するためのキーを提供できます。 SDKの作成者に、エラーの説明も送信します。
今デザート
最初に、プロジェクトでいくつの「goto」ステートメントが見つかるかを推測するように要求したことを思い出してください。 だから、あなたの数は真実とはほど遠いと思います。 合計で、プロジェクトは1,198個の gotoステートメントを使用します。 60行のコードごとに1つのgotoステートメント。 そして、私は同様のスタイルがすでに忘却に沈んでいたと思いました。
おわりに
そして、著者はこの記事で正確に何を言いたかったのですか? インテルは、PVS-Studioを慎重に検討する必要があります。 :-)