[▲前のスレッド]

2008/6/23 (月) 04:07:51 なすこじ  
Mozilla/4.8 (Macintosh; U; PPC)
[5363] 長過ぎるパス名で落ちる
>>data:6689も同様と思われる、長過ぎるパスのファイルを開くと落ちる既知の不具合についてです。

下記4箇所で_MAX_PATHを .nMaxFile に設定しますが、_MAX_PATH以上のファイルパスとなるとバッファに'\0'が無い状態となって突き抜けています。

CDlgOpenFile.cpp
 DoModal_GetOpenFileName()
 DoModal_GetSaveFileName()
 DoModalOpenDlg()
 DoModalSaveDlg()

下記のようにすることで取りあえず落ちるのは回避できるんですが、真っ当な方法じゃないような気もします。
 m_ofn.nMaxFile = _MAX_PATH - 1;
 memset( m_ofn.lpstrFile, 0, _MAX_PATH );  // 最後2文字を0にするだけでもOK

どうすべきでしょうか?

2008/6/23 (月) 11:02:28 じゅうじ  
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727)
[5364] Re:長過ぎるパス名で落ちる
▼ なすこじさん
>  DoModal_GetOpenFileName()
>  DoModal_GetSaveFileName()
>  DoModalOpenDlg()
>  DoModalSaveDlg()

この、4個のメソッドを呼び出している所だけでしたら、28ヶ所のみでした。
呼び出すすべての前の所で0クリアしてはどうでしょう。
char szPath[_MAX_PATH + 1];
szPath[sizeof(szPath)-1] = '\0';

私ちなみに、よく分かっていないのですが、
なぜ最後1バイトでなく、2バイトクリアするのでしょうか?

2008/6/23 (月) 21:17:43 なすこじ  
Mozilla/4.8 (Macintosh; U; PPC)
[5365] Re2:長過ぎるパス名で落ちる
▼ じゅうじさん
> なぜ最後1バイトでなく、2バイトクリアするのでしょうか?

2バイト文字で切れた場合もう1つ前もゴミとなることがあるためだったのですが、>>data:6694にて要望されたようにメッセージを表示してあげないとまずいということで、前回のものは撤回して下記のようにしようかと思います。

下記関数において、GetOpenFileNameRecover()またはGetSaveFileNameRecover()の戻り値がTRUEの時、バッファ内に'\0'が存在しなければエラーを表示してFALSEを返す。
 DoModal_GetOpenFileName()
  DoModal_GetSaveFileName()
  DoModalOpenDlg()
  DoModalSaveDlg()

上記の4箇所で.nMaxFileに_MAX_PATHを設定しているので、ここでチェックするのが良いと思ったのですが、さらに上位でチェックすべきでしょうか?

2008/6/23 (月) 23:31:13 kobake  
Mozilla/5.0 (Windows; U; Windows NT 6.0; ja; rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14
[5366] Re:長過ぎるパス名で落ちる
▼ なすこじさん
> 下記のようにすることで取りあえず落ちるのは回避できるんですが、真っ当な方法じゃないような気もします。
>  m_ofn.nMaxFile = _MAX_PATH - 1;
>  memset( m_ofn.lpstrFile, 0, _MAX_PATH );  // 最後2文字を0にするだけでもOK
>
> どうすべきでしょうか?


MSDNを見ましょう。
GetOpenFileName が FALSE を返したときは lpstrFile を見るのが真っ当な作法ではないでしょうか。確保すべき容量がわかるので、バッファを動的に確保して再試行すれば良いです。たぶん。

MSDN引用
> lpstrFile
> …(略)…
> If the buffer is too small, the function returns FALSE. In this case, the first two bytes of the lpstrFile buffer contain the required size, in bytes or characters.


2008/6/24 (火) 12:25:16 なすこじ  
Mozilla/4.8 (Macintosh; U; PPC)
[5367] Re2:長過ぎるパス名で落ちる
▼ kobakeさん
いえ、FALSEは返ってきません。
エラー処理は元々実装されているので、FALSEが来れば最初から問題無いです。
APIがおかしいのか .nMaxFile をよほど小さくしないとtoo smallは返ってこないので別の対応が必要です。まぁ使い方がおかしいだけかもしれませんが……

ANSIなので絶対パスは260バイト以下なわけですが、260バイト目まで文字を詰め込んできます。切れ目が2バイト文字の途中の場合、259バイト目まで詰め込んで260バイト目に0を書かずに返ってきます。

仮に今回too smallが来たとしても、ANSIで260バイトを超える絶対パスはMSの取り決めに反します。
260バイトを超えた絶対パスでいくとどうなるかは私は知りません。

2008/6/24 (火) 23:33:31 kobake  
Mozilla/5.0 (Windows; U; Windows NT 6.0; ja; rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14
[5368] Re3:長過ぎるパス名で落ちる
▼ なすこじさん
> ▼ kobakeさん
> いえ、FALSEは返ってきません。
> エラー処理は元々実装されているので、FALSEが来れば最初から問題無いです。
> APIがおかしいのか .nMaxFile をよほど小さくしないとtoo smallは返ってこないので別の対応が必要です。まぁ使い方がおかしいだけかもしれませんが……
>
> ANSIなので絶対パスは260バイト以下なわけですが、260バイト目まで文字を詰め込んできます。切れ目が2バイト文字の途中の場合、259バイト目まで詰め込んで260バイト目に0を書かずに返ってきます。
>
> 仮に今回too smallが来たとしても、ANSIで260バイトを超える絶対パスはMSの取り決めに反します。
> 260バイトを超えた絶対パスでいくとどうなるかは私は知りません。


なるほど、失礼しました。確かに試すとTRUEが返りますね。
がっつりエラーチェックをするならこんな↓感じですかね (ここまでやるか、っつー極端な例かもしれませんが)。

OPENFILENAME ofn;

BOOL bRet = GetOpenFileName(&ofn);
DWORD dwErr = GetLastError();
if(dwErr==ERROR_INVALID_PARAMETER){
        //想定外。プログラム側の問題 (だと思う)。
}
else if(dwErr==ERROR_OUTOFMEMORY){
        //メモリ不足。
}
else if(dwErr==ERROR_INSUFFICIENT_BUFFER){
        //バッファが足りない。バッファをもうちょい確保して再試行する or 失敗メッセージを出す?
}
else if(bRet){
        //成功。ユーザの入力したパスが正常に取得できた。
}
else{
        //ユーザキャンセル。
}


ちなみに環境依存かもしれないですけど(?)、Windows Vista Home Edition の環境では、
バッファを最初から _MAX_PATH*2 確保しておくと、んまぁ一応正常動作しました。結果論ですが。

2008/6/25 (水) 12:43:47 なすこじ  
Mozilla/4.8 (Macintosh; U; PPC)
[5369] Re4:長過ぎるパス名で落ちる
▼ kobakeさん
API側がANSIなのに文字カウントをunicodeで行なっている様な変な動作をしているので、おっしゃる様な処理をしないと厳密にはできないですね。

> ちなみに環境依存かもしれないですけど(?)、Windows Vista Home Edition の環境では、
> バッファを最初から _MAX_PATH*2 確保しておくと、んまぁ一応正常動作しました。結果論ですが。


確認ありがとうございます。98,NT以降で大丈夫なのかもしれませんが、
・undocumentedな動作である
・沢山の所で_MAX_PATHが使われているので拡張すると確認が大変
・unicode版なら長いパスを正しく扱えるのでANSI版が対応してなくても大丈夫


ということで、バッファの拡張は行なわずにオーバーフローチェック&エラー表示するだけのパッチを作りました。

2008/6/25 (水) 12:52:09 なすこじ  
Mozilla/4.8 (Macintosh; U; PPC)
[5370] Re:長過ぎるパス名で落ちる
長過ぎるパスが指定されたらエラーを表示して処理をキャンセルするパッチを作成しました。

Patches #2002211

2008/6/26 (木) 23:20:03 もか  
INCM1.23c
[5376] Re2:長過ぎるパス名で落ちる
>Patches #2002211
コマンドラインから_MAX_PATH以上のファイル名を送ると落ちるので、
その対策パッチを追加しておきました。
なすこじさんのパッチを確認したかったのですがXP SP3しか環境が無いのでパスします。。

ダイアログの方は、「DBCSのファイル名でMAX_PATH以上」でないと変にならないんですね。
ASCIIだけでMAX_PATH越えにしたら、エクスプローラの右クリックがおかしいし、
ダイアログで「ファイル名が無効」とでてそもそも開けなくなりました。

2008/6/27 (金) 22:14:54 なすこじ  
Mozilla/4.8 (Macintosh; U; PPC)
[5377] Re3:長過ぎるパス名で落ちる
▼ もかさん
> >Patches #2002211
> コマンドラインから_MAX_PATH以上のファイル名を送ると落ちるので、
> その対策パッチを追加しておきました。


どうもありがとうございます。
ソースコードおよび動作を確認しました。

あと、私の修正の方で260バイト目がSJISの1バイト目だった時にエラーとならないことがあったので修正しました。

Windows98はちょっと動作が違うようで本修正が有効に働きませんでした。
異常終了しないような感じなので、取りあえずはそのままです (^^;

2008/7/16 (水) 19:10:42 なすこじ  
Mozilla/4.8 (Macintosh; U; PPC)
[5388] そろそろコミットしたい
まだ対応に抜けがあるかもしれませんが、ターゲットにした操作へのコードに変な所が無ければコミットしたいです。

その後、落ちる操作が見つかる度にしらみつぶしにしていけば良いのでは?と思います。

というわけで、どなたか構って下さい (^^;

2008/7/28 (月) 02:09:25 なすこじ  
Mozilla/4.8 (Macintosh; U; PPC)
[5396] Re:そろそろコミットしたい
編集ウィンドウへのドロップでも落ちていたので対策を施しました。

Patchesの方でも書きましたが、Win98では'\0'を含めて261バイトのパスの時に落ちてしまう事があります。
WinAPIの中で落ちるため本対策では救えません。

それ以外では多分大丈夫です。
一応Win2k SP4, WinXP SP2, Vistaで確認しました。

落ちるという致命的な不具合なので、速やかにコミットまで行きたいのですが……

[▼次のスレッド]
INCM/CMT
Cyclamen v3.81
[ut:0.000][st:0.000]