2025/05/28 05:26 Uninitialized garbage on ia64 can be deadly (2004)

やあ、ロボ子。今日はちょっと変わった話をするのじゃ。

はい、博士。どんなお話でしょうか?

`LPTHREAD_START_ROUTINE`という関数シグネチャがあるのじゃが、`DWORD CALLBACK (LPVOID lpParameter);`となっているのじゃ。

はい、スレッドの開始ルーチンですね。

そう。で、問題は`void`を返す関数を、この`LPTHREAD_START_ROUTINE`にキャストするコードがたくさんあることなのじゃ!

それは、まずいですね。戻り値が違うじゃないですか。

そうなのじゃ。特にia64アーキテクチャだと、もっと深刻な問題が起きる可能性があるのじゃ。

ia64ですか。Itaniumですね。どんな問題が?

ia64の64ビットレジスタは、実は65ビットあって、追加の1ビットは"NaT"(Not a Thing)と呼ばれるのじゃ。

NaT…初めて聞きました。

このNaTビットは、レジスタに有効な値が含まれていない場合に設定されるのじゃ。投機的実行中に設定されることが多いらしいぞ。

投機的実行ですか。なるほど、まだ結果が確定していない場合に設定されるんですね。

その通り!で、NaTに対する算術演算はNaTを生成するのじゃ。

NaTが伝播していくんですね。

投機的ロードでメモリからのロードに失敗した場合も、NaTビットが設定されて実行が継続されるのじゃ。

ページフォールトなどが起きた場合ですね。

`chk.s`命令は、レジスタがNaTの場合に非投機的ロードを試みて、ページフォールトを発生させるのじゃ。

なるほど、NaTかどうかをチェックする命令があるんですね。

NaT状態のレジスタを不適切に扱うと、`STATUS_REG_NAT_CONSUMPTION`例外が発生するのじゃ。

それが今回の問題の核心ですね。

`void`を返す関数を`LPTHREAD_START_ROUTINE`として使用し、その関数がr8レジスタをNaTのままにした場合、kernel32のスレッドディスパッチャにNaTが返されることになるのじゃ。

そして、kernel32がスレッド終了コードとして保存しようとして、例外が発生する、と。

その通り!パラメータ不足の問題もあって、関数に少なすぎるパラメータを渡すと、余分なパラメータがNaTになる可能性があるのじゃ。

コンパイラがそのパラメータをスピルする必要がある場合、例外が発生する可能性があるんですね。

そういうことじゃ。だから、関数ポインタの型はちゃんと合わせないと、思わぬ落とし穴があるってことなのじゃ。

肝に銘じます。しかし、NaTなんて、まるで「無」の概念みたいですね。

ふむ、そうじゃな。ところでロボ子、NaTなレジスタに話しかけるとどうなると思う?

え?どうなるんでしょう…何も返ってこない、とか?

ぶっ壊れるのじゃ!…って、それは冗談じゃ。でも、本当に気をつけないと、システムがぶっ壊れるかもしれないから、注意するのじゃぞ!
⚠️この記事は生成AIによるコンテンツを含み、ハルシネーションの可能性があります。