-
Notifications
You must be signed in to change notification settings - Fork 163
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ファイルダイアログとフォルダ選択ダイアログでのバッファオーバーランを修正 #1599
ファイルダイアログとフォルダ選択ダイアログでのバッファオーバーランを修正 #1599
Conversation
ちょっとPRが大きくなってしまったので、どうしようか悩みましたが、とりあえずPRを投げて、分割したらいいのか、所感など様子見をしたいです。 このままで問題ないのであれば、このPRの状態でレビューをしていただして、先に進めたいと思います。 |
✅ Build sakura 1.0.3585 completed (commit 717e9765b1 by @usagisita) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PRの趣旨には賛成で、見た限りではなんとなく良さそうに見えます。
とりあえず、👇についてコメントをお願いします。
既存CodeSmellsが2万件もあるので誤差レベルといえばそうなんですけど、修正した範囲で新たに入れるものはなるたけ避けたいです(取り除くのが無理な場合もあるのでその時は「無理です」と言ってください。)
// sDefaultの先はSFilePath型 | ||
if (MAX_PATH <= sDefault.length()) { | ||
return false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
このファイルの変更は、他の変更と無関係なのでバラすことができます。(「バラせ」とは言いません。)
他の変更と異なり、かなり頑張らないとテストコードを書けない点も他と異なります。
sakura_core/env/CDocTypeManager.h
Outdated
@@ -51,7 +51,7 @@ class CDocTypeManager{ | |||
|
|||
static bool IsFileNameMatch(const WCHAR* pszTypeExts, const WCHAR* pszFileName); // タイプ別拡張子にファイル名がマッチするか | |||
static void GetFirstExt(const WCHAR* pszTypeExts, WCHAR szFirstExt[], int nBuffSize); // タイプ別拡張子の先頭拡張子を取得する | |||
static bool ConvertTypesExtToDlgExt( const WCHAR *pszSrcExt, const WCHAR* szExt, WCHAR *pszDstExt ); // タイプ別設定の拡張子リストをダイアログ用リストに変換する | |||
static bool ConvertTypesExtToDlgExt(const WCHAR *pszSrcExt, const WCHAR* szExt, std::wstring& destExt); // タイプ別設定の拡張子リストをダイアログ用リストに変換する |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static関数で内部的なインスタンス依存もないので、挙動を確認するテストを書けそうです。
引数 std::wstring& destExt
を戻り値 std::wstring
に変えてしまったほうが、呼出元コードがすっきりすると思いました(変換失敗は戻り値.empty()で判定できますし。)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
戻り値にしてみました。テストコードも追加しました。
sakura_core/CFileExt.h
Outdated
@@ -41,7 +41,7 @@ class CFileExt | |||
{ | |||
public: | |||
CFileExt(); | |||
~CFileExt(); | |||
~CFileExt() = default; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
独自デストラクタを削除できるのであれば、独自コンストラクタも削除して Rule-of-Zero に従うのがベターだと思います。SonarCloudのBugsレベル警告のうちCriticalで指摘されるものがこの件で、5種類セットで書くか何も書かないかどちらかにしないとCritical警告が出るみたいです。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
デストラクタは消して、コンストラクタはdefaultにしました。
defaultを書いておかないと、コンパイラにエラーで怒られてしまいます。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
自分が言ってたのは「何も書かないにできないか?」でした。
コンストラクタをdefaultにする必要があるなら、5種類セットで書く感じになります。
👇5種類
CFileExt() = default;
CFileExt(const CFileExt&) = delete;
CFileExt& operator = (const CFileExt&) = delete;
CFileExt(CFileExt&&) noexcept = delete;
CFileExt& operator = (CFileExt&&) noexcept = delete;
virtual ~CFileExt() noexcept = default;
あれ?6種類ある・・・。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
細かい仕様ははっきり知らなかったので、ちょっと調べてみたのですが、
http://en.wikipedia.org/wiki/Rule_of_three_%28C%2B%2B_programming%29
https://en.cppreference.com/w/cpp/language/rule_of_three
を見たところ「普通のコンストラクタ」が5個には含まれない、つまりコンストラクタは存在していていい、ということみたいですね。
Rule of zeroのところにコンストラクタだけ宣言されています。
書くなら全部かけRule of fiveは、デストラクタを書くなら、コピーとムーブ演算子を定義するかdeleteする、という意味ですね。コピーやムーブ以外の、普通のコンストラクタは無関係かと。
・マクロでフォルダを開く初期フォルダ制限 ・pszUserWildCardがSFilePathでMAX_PATH制限 ・CFileExtのExtが短い固定長だったのをwstringへ変更
・バッファオーバーランしていたのを修正
dfa3f75
to
60008c8
Compare
なんか単体テストがエラー吐いていて、ダメみたいですね。 |
❌ Build sakura 1.0.3588 failed (commit 71b3f7465e by @usagisita) |
vs2019 を使って x64 - Debug ビルドしたモジュールで動かしたらテスト失敗が再現しました。 これ。 |
テストがクラッシュする原因は、CDlgOpenFile_CommonItemDialogのコンストラクタが たぶんテストを書けるのは |
テスト失敗の対策案を出しときます。 TEST(CDlgOpenFile, Construct) 👇 TEST(CDlgOpenFile, DISABLED_Construct) 他の |
コメントありがとうございます。m_pShareDataの初期化を遅延させればと浅知恵しましたが、Createの後はいくつかの関数に分かれて呼び出されるため、バラバラに書くのも微妙だなという結論に達しました。 |
✅ Build sakura 1.0.3595 completed (commit 330d80cf65 by @usagisita) |
✅ Build sakura 1.0.3597 completed (commit 20e66183de by @usagisita) |
Code Smellsのうち1件は直前の修正のミスだったので、対応しました。 |
sakura_core/env/CDocTypeManager.cpp
Outdated
@param szExt [in] リストの先頭にする拡張子 例「.h」 | ||
@param pszSrcExt [in] 拡張子リスト 例「.c cpp;.h」(ドットはオプション) | ||
@param szExt [in] リストの先頭にする拡張子 例「.h」(ドット必須) | ||
@param destExt [out] 拡張子リスト 例「*.h;*.c;*.cpp」 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
最後の @param destExt ...
の所は @return
になると思います。
TEST(CDocTypeManager, ConvertTypesExtToDlgExtThree) | ||
{ | ||
const std::wstring expected = { L"*.txt;*.cpp;*.h;*.hpp" }; | ||
std::wstring actual = CDocTypeManager::ConvertTypesExtToDlgExt(L"cpp;h;hpp", L".txt"); | ||
EXPECT_EQ(expected, actual); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pszSrcExt の中に szExt の拡張子を含むパターン (例:ConvertTypesExtToDlgExt(L"txt;cpp;h;hpp", L".txt");
) も用意されていた方が良いのではと思いました。
✅ Build sakura 1.0.3601 completed (commit 45efb69175 by @usagisita) |
SonarCloud Quality Gate failed. |
✅ Build sakura 1.0.3602 completed (commit aa426b6117 by @usagisita) |
長くお付き合いくださり、ありがとうございます。 |
あえて揚げ足取っときますが マージ ですね。 git commit = 変更をローカルブランチに保存する。 |
PR の目的
ファイルダイアログ、フォルダダイアログへ長い文字列を指定するとバッファオーバーランする不具合の修正をします。
カテゴリ
PR の背景
極限値までいかなくても、ちょっと拡張子リストが長かったりすると、おかしいようなので、対策をします。
PR のメリット
バグが減ります。
PR のデメリット (トレードオフとかあれば)
ほとんどないと思います。
自動テストがないので、コードレビューが面倒です。
仕様・動作説明
「名前を付けて保存」ダイアログで、ファイルの種類の拡張子リストが正常に表示されないまたはクラッシュするのが修正されます。
保存ダイアログでは「ファイルの種類」で「ユーザ指定(*.txt:*.type;*.*)」と「テキストファイル(*.*)」と「すべてのファイル(*.*)」が選べます。
そのユーザ指定には今、開いているファイルの拡張子と現在のタイプ別設定が「ユーザ指定(*.open_file_ext:*.txt;*.log;*.*)」のように一度に表示されますが、長さ制限が足りていません。
開いているファイルの拡張子と、タイプ別設定に登録された拡張子リストが合わせてかなり長い場合、異常終了していました。
また同様に以下のマクロを実行する時も異常終了していました。
以前は落ちていたので実質的な影響はないと思います。
Editor.FileOpenDialog
/FileSaveDialog("file_name")
の指定にMAX_PATH
制限を導入します。Editor.FileOpenDialog("file_name.txt", "*.ext_filter")
のext_filter
が190文字前後以上長いとバッファオーバーランしてどこかおかしな動作をするのが修正されます。Editor.FolderDialog("説明","default_folder")
のdefault_folder
にMAX_PATH
制限を導入します。PR の影響範囲
元の制限は
CFileExt::FileExtInfoTag::m_szExt[MAX_TYPES_EXTS*3+1]
MAX_TYPES_EXTS
は64なので193文字(NULL含む)まで入力可能でした。それ以上はチェックされずにオーバーランします。
こちらは保持する配列ごと
std::vector'とstd::wstring
によりリファクタリングしました。sakura/sakura_core/CFileExt.cpp
Line 85 in 95d9b03
CDlgOpenFile_CommonFileDialog
とCDlgOpenFile_CommonItemDialog
のm_szDefaultWildCard
はSFilePath
なのでMAX_PATH
制限があります。こちらも異常終了する原因です。
こちらも
std::wstring
に変更しました。sakura/sakura_core/dlg/CDlgOpenFile_CommonItemDialog.cpp
Line 438 in 95d9b03
sakura/sakura_core/dlg/CDlgOpenFile_CommonFileDialog.cpp
Line 690 in 95d9b03
呼び出し側の関連処理では
CDocFileOperation.cpp
のCDocFileOperation::SaveFileDialog()
では_MAX_PATH+10
文字の制限がありました。sakura/sakura_core/doc/CDocFileOperation.cpp
Lines 228 to 254 in 95d9b03
ConvertTypesExtToDlgExt(type.m_szTypeExts, szExt, szDefaultWildCard)
szExt
にファイルの拡張子を指定して、タイプ別設定と一緒にデフォルトフィルターに指定しています。ローカル変数
szDefaultWildCard
に必要とされる最大値はMAX_PATH+MAX_TYPES_EXTS*2
前後で足りていません。m_szTypeExtsは「
a;b;c
」「.a;.b;.c
」→「*.a;*.b;*.c
」のように展開されるので最大でも約2倍必要です。PRでは細かいことは気にせず
std::wstring
に変更しています。CDocTypeManager::ConvertTypesExtToDlgExt
では書き込みバッファの最大値がわからないため、std::wstring
で返すようにリファクタリングしました。テスト内容
共通設定ー編集「Vistaスタイルのファイルダイアログ」をON/OFFすると表示に使う実装クラスが異なるため、ON/OFF2回ずつテストが必要です。
自動テストがなくて申し訳ないです。
テスト1
手順
下記マクロを実行して、異常終了しないことを確認します。
マクロで
FileOpenDialog
/FileSaveDialog
のファイル名のデフォルト指定が長いと異常終了していたのが、マクロ中断のエラーダイアログが表示されるようになります。テスト2
手順
同マクロで、拡張子フィルターが長いと異常終了していたのが、直ります。
特にいまのところ拡張子フィルターの文字列長に制限はありません、
テスト3
手順
フォルダを開くマクロのデフォルト指定がMAX_PATHを超えていたときに落ちていたのが、マクロ中断のエラーダイアログが表示されるようになります。
テスト4
タイプ別設定の拡張子に以下を設定:
a;b;c;d;e;f;g;h;i;j;k;l;m;n;o;p;q;r;s;t;u;v;w;x;y;z;0;1;2;3;4;5
拡張子が異常に長いファイル(存在していなくてもいい)を開きます。
ファイル名例:
D:\testfile.long_long_ext_filter_01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234
上記のタイプ別を一時適用します。
「名前を付けて保存」でダイアログを表示します。
落ちないか確認します。
また拡張子リストが正しく表示されるか確認します。
ダイアログを閉じた後に異常終了することがありました、のでそこまで確認します。
関連 issue, PR
参考資料