Skip to content
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

#780 でのCNativeW の初期状態での仕様を取り消して、 #948 の不具合を修正する #949

Merged
merged 11 commits into from
Jun 29, 2019

Conversation

m-tmatma
Copy link
Member

PR の目的

#780 でのCNativeW の初期状態での仕様を取り消して、 #948 の不具合を修正する

カテゴリ

PR の背景

#780#780 (comment) に対応した結果CNativeW の初期状態での仕様が変更になってしまった。
CNativeW の初期状態での仕様に依存したコードによって #948 の不具合が発生した。

PR のメリット

#948 の不具合を修正できる。

PR のデメリット (トレードオフとかあれば)

特になし

PR の影響範囲

CNativeW の初期状態でバッファが確保されていないことに依存したコード

関連チケット

#780
#948

参考資料

なし

変更手順

  1. バッファが空の状態で CNativeW::Clear を呼び出したときに落ちる不具合修正 #780 が master にマージされた e225bc1 を revert する
  2. e225bc1 のうち、単体テストが revert されたのを戻す (再度登録する)
  3. fcb340b を cherry-pick する

@m-tmatma m-tmatma added the 🐛bug🦋 ■バグ修正(Something isn't working) label Jun 14, 2019
@m-tmatma m-tmatma added this to the v2.4.0 milestone Jun 14, 2019
tests/unittests/test-cmemory.cpp Show resolved Hide resolved
tests/unittests/test-cnative.cpp Show resolved Hide resolved
sakura_core/mem/CMemory.cpp Show resolved Hide resolved
{
assert(m_nRawLen <= m_nDataBufSize-2);
m_nRawLen = nLength;
assert(m_nRawLen <= m_nDataBufSize-2);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

389行目と387行目の違いはなんでしょう?
この話も別件でよいです。

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

388 行目を実行する前と後で両方とも成立するか確認しています。

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コメント足しました

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここ、本当はこういうことだと思います。

if (m_nDataBufSize - 2 <= nLength) throw std::out_of_range("invalid arg");
if (m_nDataBufSize - 2 <= m_nRawLen) throw std::runtime_error("invalid state");
m_nRawLen = nLength;

コード記述上、代入してからチェックしてるので同じ書きっぷりのassertですが、
実態は引数チェックとメンバ変数の状態チェックだと思うんです。

変更後のコードに新しいコメント付けましたが、コードにコメント付けちゃうと「めんどいから後回し!」の大義名分を失うような気がしています。

m_pRawData[m_nRawLen+1]=0; //終端'\0'を2つ付加する('\0''\0'==L'\0')。
if (m_nDataBufSize > 0)
{
assert(m_nRawLen <= m_nDataBufSize-2);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

メモ:これはClear関数呼出の前提条件を外すための内部仕様変更。

変更前:利用中サイズが確保済みサイズ未満であればクラッシュ。
変更後:確保済みサイズが0より大きい場合、利用中サイズが確保済みサイズ未満であればクラッシュ。

assert(m_nRawLen <= m_nDataBufSize-2);
m_pRawData[m_nRawLen ]=0;
m_pRawData[m_nRawLen+1]=0; //終端'\0'を2つ付加する('\0''\0'==L'\0')。
if (m_nDataBufSize > 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この条件だと「(メモリ確保状態に関係なく)確保済みサイズが0より大きい場合」と読めます。
個人的な感覚では、if (m_pRawData && m_nDataBufSize) が適切な条件だと思います。

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

チェックを足しました。

{
assert(m_nRawLen <= m_nDataBufSize-2); // m_nRawLen を変更する前に必要な条件が成立しているか確認する
m_nRawLen = nLength;
assert(m_nRawLen <= m_nDataBufSize-2); // m_nRawLen を変更した後も必要な条件が成立しているか確認する
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

「既存コードそのままインデントしただけなんで、細かいツッコミは勘弁してください」ならassert条件の精査は要らないと思っています。コメント足してしまうと、中身を理解した上でこれでおっけー、という意味になっちゃうような気がします。別件でいいと思っていたassert条件の精査を、いまやんないといけなくなる気がします。

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

やらなくていいかどうかは置いておいて、ロジック自体は簡単です。

m_nRawLen が実際のデータサイズで、それに 終端'\0'を2つ付加しているので
その分の 2 を考慮してサイズが OK かチェックしています。

まあ -2 は左辺に持ってきたほうがいいかもしれませんが。

memcpy( &m_pRawData[m_nRawLen], pData, nDataLen );
m_nRawLen += nDataLen;
m_pRawData[m_nRawLen] = '\0';
m_pRawData[m_nRawLen+1] = '\0'; //終端'\0'を2つ付加する('\0''\0'==L'\0')。 2007.08.13 kobake 追加

assert(m_nRawLen <= m_nDataBufSize-2);
m_nRawLen = nLength;
assert(m_nRawLen <= m_nDataBufSize-2);
m_pRawData[m_nRawLen ]=0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

えいやっ!でapproveするタイミングを逸したので、
あらためて元のコードをよく見てみます。

PRの内容は、既に修正目的を果たしているように思うんですが、
メソッドの元々の目的と修正目的を合わせてみたときに「適切な変更かどうか」に疑問があります。

このメソッドでやりたいことは次の2つです。

  • 387行目 利用中サイズ引数「新しいサイズ」を代入する。
  • 389行目 確保済みバッファ引数「新しいサイズ」の位置に文字列終端を書き込む。

他の3行はオマケです。エロい人にはそれが分からんのです・・・が、
386行目のassertが原因でメモリ確保前にClear()してはならないという仕様になっていました。
メモリ確保前はClear()できないとすると、Clear()の前に毎回チェックが必要になります。
これはめんどくさいので、メモリ確保状態に関係なく呼出できるように改善しようとしてます。

今回の修正の第1目的は、メモリ確保前でもClear()できるようにするだと思っています。
PRでは、既存の処理を if (メモリ確保後) で括ることによってこれを実現しています。
これはこれで正しいように思っています。

しかし、ちょっと待ってください。
目的が メモリ確保前でもClear()できるようにする なのに、なんで確保後かどうかを判定してるんでしたっけ?
386行目の前に確保前なら抜けるを足してやればいいような気がします。

if (!m_pRawData || !m_nDataBufSize) {
	assert(!nLength);
	return;
}

※コードは動確しとりません。

Copy link
Member Author

@m-tmatma m-tmatma Jun 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

↑ 条件判定文は判定条件が逆になっていますが、内容的にはこの PR の最新のコードとほぼ同じです。
(m_nDataBufSize が int なので負の場合を考慮していないという点が違いますが)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

既存のコードを変更せずに追加になるので、提案の方法のほうが差分確認しやすいという面はありますが。

m_nRawLen = nLength;
assert(m_nRawLen <= m_nDataBufSize-2);
m_pRawData[m_nRawLen ]=0;
m_pRawData[m_nRawLen+1]=0; //終端'\0'を2つ付加する('\0''\0'==L'\0')。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(別件です。)

このコードの有効性に疑問があります。

引数nLengthに偶数を指定した場合には一定の意味があります。
ANSIバッファとして書き込んだバイナリ列をUNICODEバッファとして解釈したい場合、UNICODEのNULは2バイトなので2個目のNULを書く意味があります。

引数nLengthに基数を指定した場合にはやや微妙だと思います。
ANSIバッファは1バイトずつアクセスできますが、UNICODEバッファは2バイト単位でアクセスします。WORD境界のアラインメントを無視して2個目のNULを書く意味はありません。どうせやるなら3個目のNULの要否判断も組み込んで、有効データの後ろに1つ以上の(WORD)NULが含まれることを保証したほうがいいように思います。

 あふぉ   ← 6バイトのsjis文字列
  ↓ 半角カナに変換して _SetLength(3) する
 アフォ␀␀ア  ← 6バイトのsjis文字列

@berryzplus
Copy link
Contributor

このPRのステータス。
以下いずれかのアクションを待っています...

  1. 「メモリ確保前でもClearできるように」を実現する対応で「メモリ確保後か?」を判定する「もっともらしい理由」の提示
  2. 「メモリ確保前か?」を判定する、最小限の改修に変更
  3. 他レビュアーのコメント待ち

個人的には 3. で構わないと思っています。
「俺はこれでいい」と approve する人が出てもよいです。

Copy link
Contributor

@berryzplus berryzplus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

対応ありがとうございます。

assertに付けたコメントは、このまま「追加」でよいと思っています。
「NULを2個付けるコードの有効性」に関する疑問は別途検証でよいような気がしています。

@m-tmatma m-tmatma merged commit abf90da into sakura-editor:master Jun 29, 2019
@m-tmatma m-tmatma deleted the feature/fix-up-PR780 branch June 29, 2019 05:16
HoppingTappy pushed a commit to HoppingTappy/sakura that referenced this pull request Jun 16, 2020
sakura-editor#780 でのCNativeW の初期状態での仕様を取り消して、 sakura-editor#948 の不具合を修正する
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🐛bug🦋 ■バグ修正(Something isn't working)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants