HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2020
0816
masakainstr関数がバッファオーバーランを発生させるバグ?5解決


masaka

リンク

2020/8/16(Sun) 01:22:40|NO.91178

変数の終端文字がShift-JISの全角文字の1バイト目だった場合に、文字列変数の前回の内容が
instr関数の検索対象に入ってしまうバグを発見しました。
(HSP3.51とHSP3.6β3で同様の現象が発生することを確認)

例えば、下記の例では「ほげほげふがふが」という8バイトの内容が書かれた"hoge.txt"を
先頭5バイトだけ読み込んだ場合に、前回読み込まれた内容を含めて検索してしまうようです…

// "ほげほげふがふが"という内容の hoge.txt を用意
_buf="ほげほげふがふが" notesel _buf notesave "hoge.txt" // 先頭5バイトだけ読み込む buf="" notesel buf noteload "hoge.txt", 5 mes buf mes "strlen:" + strlen(buf) + ", instr(ふが):" + instr(buf, 0, "ふが") + "\n" // 全部読み込む noteload "hoge.txt" mes buf mes "strlen:" + strlen(buf) + ", instr(ふが):" + instr(buf, 0, "ふが") + "\n" // 再度、先頭5バイトだけ読み込む → instr関数が前回読み込んだ内容を読んでしまう noteload "hoge.txt", 5 mes buf mes "strlen:" + strlen(buf) + ", instr(ふが):" + instr(buf, 0, "ふが") + "\n"

実行結果:
ほげ€
strlen:5, instr(ふが):-1 ほげほげふがふが strlen:16, instr(ふが):8 ほげ€ strlen:5, instr(ふが):8
(※本来、最後の行は「strlen:5, instr(ふが):-1」となるはず)

はじめは、Shift-JISの全角文字の1バイト目'\x82'のあとのNULL文字が無視されているのかと予想しました。
ただ、その場合は最後のbufのメモリ内容は"ほげほげ\x82\0がふが"となり、instr関数の結果は
8ではなく12になる(後ろの方の"ふが"がヒットする)はずなので、単純にinstr関数でNULL文字が
無視されているという訳でもなさそうです。(そもそもnoteload命令はNULL文字を明示的に挿入していない?)

半端なサイズで読み込んだファイル内容をShift-JIS文字列が前提のinstr関数にかけるのが悪いのですが、
instr関数以外に良い検索命令がないもので、少し困っています。
(具体的には、上記プログラムと似たような感じで、テキスト形式で書かれた巨大なファイルの先頭の数百byteだけ読み出して、
ヘッダ部分が全部読み込めたかどうかを調べるために、ヘッダの終端文字列が含まれているかどうか確認する処理に使っています。)



この記事に返信する


masaka

リンク

2020/8/16(Sun) 01:41:24|NO.91179

(実際に動かしてみてはないですが、)OpenHSP内でのnoteload関数の実装を確認する限り、ちゃんと「ptr[size] = 0;」という部分でNULL文字を挿入しているみたいですね…
instr命令に関しては、単純にchar*型の文字列に対して1バイトずつずらしながら比較しているだけだったので、NULL文字が挿入されていないことがこのバグの原因のようではあるのですが…

noteload関数の実装:
https://github.com/onitama/OpenHSP/blob/64f6eaca9935062a8aa2107653e67728298d18c0/src/hsp3/hsp3int.cpp#L1020

case 0x25: // noteload { int size; char *ptr; char *pdat; code_event( HSPEVENT_FNAME, 0, 0, code_gets() ); p1 = code_getdi( -1 ); code_event( HSPEVENT_FEXIST, 0, 0, NULL ); size = ctx->strsize; if ( size < 0 ) throw HSPERR_FILE_IO; if ( p1>=0 ) if ( size >= p1 ) { ctx->strsize = size = p1; } pdat = note_update(); HspVarCoreAllocBlock( ctx->note_pval, (PDAT *)pdat, size+1 ); ptr = (char *)note_update(); code_event( HSPEVENT_FREAD, 0, size, ptr ); ptr[size] = 0; break; }



masaka

リンク

2020/8/16(Sun) 01:57:42|NO.91180

ちなみに、半角文字だと特にこのバグは発生しない模様

// "hogehogefugafuga"という内容の hoge.txt を用意 _buf="hogehogefugafuga" notesel _buf notesave "hoge.txt" // 先頭5バイトだけ読み込む buf="" notesel buf noteload "hoge.txt", 5 mes buf mes "strlen:" + strlen(buf) + ", instr(fuga):" + instr(buf, 0, "fuga") + "\n" // 全部読み込む noteload "hoge.txt" mes buf mes "strlen:" + strlen(buf) + ", instr(fuga):" + instr(buf, 0, "fuga") + "\n" // 再度、先頭5バイトだけ読み込む noteload "hoge.txt", 5 mes buf mes "strlen:" + strlen(buf) + ", instr(fuga):" + instr(buf, 0, "fuga") + "\n"

実行結果:

hogeh strlen:5, instr(fuga):-1 hogehogefugafuga strlen:16, instr(fuga):8 hogeh strlen:5, instr(fuga):-1



masaka

リンク

2020/8/16(Sun) 02:03:41|NO.91181

すみません、勘違いでした。
先頭9バイトではなく先頭5バイトを読み込んでいるので、"ほげほげ\x82\0がふが"ではなく"ほげ\x82\0げふがふが"になりますね。
その場合は2番目の「instr(ふが):8」は特におかしくはなく、noteload命令はちゃんとNULL文字を挿入してくれていそうです。

単純にinstr関数が'\x82'のあとのNULL文字をチェックしてくれていない可能性が高そうです。



masaka

リンク

2020/8/16(Sun) 04:14:50|NO.91182

instr関数の内部で呼ばれているstrstr2関数の実装の問題でした。
マルチバイト文字の途中にNULL文字があっても、読み飛ばしてしまうようでした。

Win32版(Shift-JIS)・Linux版(UTF-8)の両方で修正の動作確認が取れたので、GitHubの方のOpenHSPへプルリクを投げておきました。
https://github.com/onitama/OpenHSP/pull/11



masaka

リンク

2020/8/16(Sun) 04:37:49|NO.91183

修正までは以下のコードをプログラムの先頭に入れて公式のinstr関数を置き換えるようにしておけば、
instr関数でとりあえず正しい結果が得られるようにはなると思います。

#undef instr #module __instr__ #defcfunc local instr_ var p1, int p2, str p3, local ret ret = instr@hsp(p1, p2, p3) if (ret >= strlen(p1)) : return -1 return ret #define global ctype instr(%1,%2=0,%3) instr_@__instr__(%1,%2,%3) #global



ONION software Copyright 1997-2023(c) All rights reserved.