-
Notifications
You must be signed in to change notification settings - Fork 162
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
正規表現キーワードのインポートで許容サイズを超える文字列を無駄にコピーしているのを修正する #1244
正規表現キーワードのインポートで許容サイズを超える文字列を無駄にコピーしているのを修正する #1244
Conversation
✅ Build sakura 1.0.2713 completed (commit 7adade8293 by @berryzplus) |
7e122ca
to
1b4f138
Compare
✅ Build sakura 1.0.2715 completed (commit 894193e528 by @berryzplus) |
@@ -661,11 +661,14 @@ bool CImpExpRegex::Import( const wstring& sFileName, wstring& sErrMsg ) | |||
} | |||
if( k != -1 ) /* 3文字カラー名からインデックス番号に変換 */ | |||
{ | |||
if( 0 < MAX_REGEX_KEYWORDLISTLEN - keywordPos - 1 ){ | |||
// pKeywordに書き込める残りサイズ(NUL終端分を含む) | |||
const size_t cchAvailableSize = MAX_REGEX_KEYWORDLISTLEN - keywordPos; |
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.
ここって念の為
const size_t cchAvailableSize = MAX_REGEX_KEYWORDLISTLEN - 1 - keywordPos;
にした方が良いのかも?と思いました。681行目 でNULL終端を追記しているので。
入力用意して動作確認をまだしていないので不確かですが…。
それはともかく、maxdata.h
にある下記の記述を見てコメントが無いので何が何やら(どう使い分ければ?)という感じですw まぁ連番とかにされないだけ全然マシなのかもしれませんけど。
MAX_REGEX_KEYWORD = 100, //@@@ 2001.11.17 add MIK
MAX_REGEX_KEYWORDLEN = 1000,
MAX_REGEX_KEYWORDLISTLEN = MAX_REGEX_KEYWORD * 100 + 1,
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.
にした方が良いのかも?と思いました。681行目 でNULL終端を追記しているので。
ええ、そうなんですが大丈夫だと思います。
NUL終端する位置 keywordPos
を増加させるコードは、
667行目の if( 0 < cchAvailableSize - 1 )
が成立しない場合実行されないからです。
バッファは pKeyword
は std::make_unique<wchar_t[]>(MAX_REGEX_KEYWORDLISTLEN);
で確保されているので MAX_REGEX_KEYWORDLISTLEN
は 終端NUL を含むバッファサイズです。
auto szKeyWordList = std::make_unique<wchar_t[]>(MAX_REGEX_KEYWORDLISTLEN); |
std::min
で比較する相手 MAX_REGEX_KEYWORDLEN
も 終端NUL を含むバッファサイズで、NULを含むサイズを使う箇所2箇所 vs NULを含まないサイズを使う箇所1箇所だったので NULを含むサイズのほうで const 切った感じです。
regexKeyArr[count].m_nColorIndex = k; | ||
wcsncpy_s( &pKeyword[keywordPos], MAX_REGEX_KEYWORDLISTLEN - keywordPos, p, _TRUNCATE ); | ||
wcsncpy_s( &pKeyword[keywordPos], std::min<size_t>(MAX_REGEX_KEYWORDLEN, cchAvailableSize), p, _TRUNCATE ); |
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.
@usagisita さんが指摘コメント #1034 (comment) で言ってた「元のコードが 完全に正しいかは別 にして」と言ってたのは、たぶんココのことです。
- 元コードは1キーワードあたりのバッファサイズ
MAX_REGEX_KEYWORDLEN
と書き込んでよい残り文字数 ‘MAX_REGEX_KEYWORDLISTLEN - keywordPos - 1` を比較して小さいほうをバッファサイズとしています。 - 変更後コードは1キーワードあたりのバッファサイズ
MAX_REGEX_KEYWORDLEN
と書き込んでよい残りバッファサイズ ‘MAX_REGEX_KEYWORDLISTLEN - keywordPos` を比較して小さいほうをバッファサイズとしています。
ちゃんと検証してないですが、元コードの実装はマズそうです。
(実害があるのかどうか微妙なレアケースになりますが。)
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.
まずいのは元コードですか?
MAX_REGEX_KEYWORDLISTLEN の最後まできっちり書き込んだときに
pKeyword[keywordPos] = L'\0'; |
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.
あ、周回遅れでした。>#1244 (comment)
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.
しかしこの答えは答えになっていないと思う。>#1244 (comment)
NUL終端する位置 keywordPos を増加させるコードは、 667行目の if( 0 < cchAvailableSize - 1 ) が成立しない場合実行されないからです
cchAvailableSize が 12 以上あるときに cchAvailableSize がヌル文字を含んだ上限として wcsncpy_s が実行されるのでは。
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.
必要かどうかは知らないが MAX_REGEX_KEYWORDLISTLEN で確保された配列はヌル文字単独だけを書き込まれるか1つ以上のヌル終端正規表現キーワードプラスヌル文字になるように書かれていた、たぶん。
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.
MAX_REGEX_KEYWORDLISTLEN の最後まできっちり書き込んだときに
これはその通り!
PRしてる修正内容だとマズいっすね。
修正します。
UI側では入力できる文字数を 999 文字までに制限しています。 sakura/sakura_core/typeprop/CPropTypesRegex.cpp Lines 540 to 547 in 941ca08
NULL終端含めないで1000文字以上のキーワードは長すぎるので自動で切り詰めて読み込むより、エラーメッセージをLSマクロで読んで設定して |
PRのタイトルの 確認に使用したファイル : test.rkw.txt あまり長すぎると なおリストビューのアイテムには設定したデータのうち先頭の 259 文字分までしか表示されないようです。 https://docs.microsoft.com/en-us/windows/win32/api/commctrl/ns-commctrl-lvitema あと前のコメントに書きましたがエディット部分にはNULL終端を除いて999文字までで切り詰められます。 |
@@ -661,21 +661,27 @@ bool CImpExpRegex::Import( const wstring& sFileName, wstring& sErrMsg ) | |||
} | |||
if( k != -1 ) /* 3文字カラー名からインデックス番号に変換 */ | |||
{ | |||
if( 0 < MAX_REGEX_KEYWORDLISTLEN - keywordPos - 1 ){ | |||
// pKeywordに書き込める上限サイズ(NUL終端分を含む) | |||
const size_t cchAvailableSize = std::min<size_t>( MAX_REGEX_KEYWORDLEN, MAX_REGEX_KEYWORDLISTLEN - 1 - keywordPos ); |
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.
変数名とコメントが嘘です。MAX_REGEX_KEYWORDLEN は pKeyword からくる制約ではありません。
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.
符号なし整数に負の値を設定しようとするケースがあります。
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.
符号なし整数に負の値を設定しようとするケースがあります。
考えてなかったです。
✅ Build sakura 1.0.2717 completed (commit 5a67b4bcb9 by @berryzplus) |
これをやるべきか否か思案中・・・ いや、やるべきなんだろうけどメッセージ追加するのが意外とめんどいw テスト用サンプル正規表現キーワードセット作りました。 |
sErrMsg = LS(STR_IMPEXP_REGEX3); | ||
} | ||
} | ||
} | ||
pKeyword[keywordPos] = L'\0'; | ||
pKeyword[keywordPos] = L'\0'; // 2個目のNUL終端を書き込む |
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.
必ずしも2個目ではありません。
「長過ぎる」メッセージを追加したバージョンをpushしました。
タイトル微妙に変えました。 確かに落ちないっすね。 PRタイトルもう1回編集しなきゃですね・・・。 |
✅ Build sakura 1.0.2718 completed (commit b71f90412d by @berryzplus) |
うん、現master(941ca08) でやっぱり落ちない。
グダグダな感じに整理すると、こんなん。
で、どうするか。
ちょっと変えてみて、文句、いちゃもん他いろいろを募集する感じにします。 |
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.
コピーしてから長さ調べるんじゃなくて、コピー前に長さ調べて長過ぎたらコピーせずに キーワードが長過ぎる
というメッセージをセットして return false;
するべきだと思います。
UIで設定出来ない不正な長さのデータ、つまり通常にエクスポートした場合で作れないデータは不正扱いしてよいと思います。
同意です!と直しかけましたが、ちょっと提案があります。 今回対処するのは 既存の取り込みエラーに対するエラー処理が、 無駄なコピーはしないは賛成なのでコピー前にチェックは入れたいです。 |
✅ Build sakura 1.0.2720 completed (commit ac99d5177f by @berryzplus) |
確かに従来の記述でも return false; はあんまりしてないですね。なんか判定追加の分だけ記述が冗長になるのはうーむ…ですがしょうがないのかもですね。 個人的に気になるのは複数のキーワード行でイレギュラーが有った場合に後続の処理でエラーメッセージが上書きされることでしょうか。気にするだけ損かもですが…。 あとインポート処理って既存のリストに追加じゃなくて入れ換え動作っぽいですね。あまり自分がふだんいじらないところなので思わぬ挙動にびっくりする時が有ります。 |
count++; | ||
keywordPos += wcsnlen( &pKeyword[keywordPos], MAX_REGEX_KEYWORDLISTLEN - keywordPos ) + 1; | ||
// pに入っている文字列の長さ | ||
const auto ncpyLength = ::wcsnlen( p, MAX_REGEX_KEYWORDLEN ); |
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.
行のサイズってここ のインスタンスに本来入っているので、メモリスキャンする wcsnlen
とかの関数を呼び出すのは本来は良くないと思うんですよね。とはいえ元コードが無駄に行バッファにコピーしてそこから wcslen
呼んでたりするんで 俺のせーじゃねーよ!
と言われたらそうなんですが…。
なんかもうこのメソッドの実装ってぐだぐだなので全体を書き直したいです。まぁテストしないとバグだらけになる未来が見えますが…。:sob:
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.
行のサイズって line.length()
のことですかね?
ローカル変数 line
の内容はココ(↓)のコメントの内容になってます。
//RxKey[999]=ColorName,RegexKeyword |
で、ココのコードの時点で ローカル変数 p
には RegexKeyword
が入ってます。
WCHAR *p = wcsstr(&buff[11], L","); |
なので、 line.length() - p - buff
としたら p
の長さは取れると思います。
そうかくと「なんでやねん!」ってなると思ったので、
編集前コードにあった wcsnlen
を使う構成にしてみました。
とはいえ元コードが無駄に行バッファにコピーしてそこから wcslen 呼んでたりするんで 俺のせーじゃねーよ! と言われたらそうなんですが…。
実は、編集できる文字数までで切り捨てるなら、コピーは無駄じゃない気もしています。
設定ダイアログ側に「エラーパターンを強調表示する」ような拡張が必要ですが、
登録せずに捨てちゃうと「どのパターンが切り捨てられたか」が分かんないので:smile:
なんかもうこのメソッドの実装ってぐだぐだなので全体を書き直したいです。まぁテストしないとバグだらけになる未来が見えますが…。
メソッド単位でみると意外とまともだと思います:smile:
基底クラスで定義したインターフェースが利用実態とあってないので継承パターンなのに同じコードをコピペして回った感じになってるあたりに不満がありますが、言うほどgdgdでもない印象です。
C++
のコードは単体テストが書けるコードと単体テストを書けないコードに分かれると思っていますが、このあたりのコードは後者だと思います。
テストが書けないから「動かしてみる」以外の方法で検証ができず、書かれたテストコードがないから「検証方法が妥当か?」の検証もできず、結果としてバグが入りこむことになる、という考え方に基づいて「テスト可能な処理を少しずつ切り出してみよう」と思っています。
まあ、要約すると「そのあたりは別件で。」になるわけですが:smile:
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.
不正なデータが含まれてたらその旨を表示してインポート処理をしない、という処理が単純で良いと思いますね。異常データに手厚いケアを行うコスト掛けるのはもったいない気がします。
この機能やユースケースに自分が思い入れが少ないのであまり異常データを救おうという気になれないです。
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.
問題無いと思います。
#1244 (comment) のコメントにあったファイルを使って aaad252 で動作確認は行いました。
ちょっと気になるのがキーワードが長すぎる場合に結局追加はしない実装になっていますが、 |
現状で「落ちない」ことが確認できたので |
こっち #1244 (comment) にも書いたんですが、もしかしたら「まるまる捨てたらあかん」のかも知れません。 「切り捨てました」って出たら「どれや?」って確認したくなるのが人情で、 |
対策を含めようとして いったん、「長過ぎたら取り込まない」で進めておくことにします。 |
レビューありがとうございます。 |
正確には、既存コードでは1キーワードあたりのサイズオーバーの判定は行ってなくて、サイズオーバー判定を行ってないから切り捨てを検出せずに「切り捨てられた状態のキーワード」が登録されるという挙動です。 |
sakura-editor#1244 (comment) なんか違う気がしてきたのでボツにします。
…r1034_buffer_size_limitation 正規表現キーワードのインポートで許容サイズを超える文字列を無駄にコピーしているのを修正する
PR の目的
PR #1034 で入れた不具合を修正します。
もともと入れてしまった不具合は
正規表現キーワードの1個あたりの最大長を超える文字列は切り詰める。
という処理を外してしまったことです。
カテゴリ
PR の背景
#1234 のバグ報告が入った。
↓
PR #1034 に問題があると分かった。
↓
#1034 (comment)
↓
PR向けの修正を作成。
動作確認にて、#1238 のマージが必須であると判明。
↓
いったんDraft版でPRを作り、 #1238 のマージ後にrebaseしてDraft解除します。
↓
マージされたのでrebaseしてDraft外します。
v2.4.1リリース作成にあたって必須の変更と考えられます。
PR のメリット
PR のデメリット (トレードオフとかあれば)
PR の影響範囲
関連チケット
参考資料