int main() { /* NEPプログラミング部 */ } ID:8qDetHhm

37以下、名無しにかわりましてVIPがお送りします:2024/07/21(日) 01:32:32.24 ID:8qDetHhm

金曜に騒ぎになってたブルスクの直接的な原因がメモリの0x9c番地にアクセスしようとした結果のクラッシュだと判明して、これがnullポインタ周りのバグからじゃないかと言われてるのでちょっと解説してみる

39以下、名無しにかわりましてVIPがお送りします:2024/07/21(日) 01:48:22.55 ID:8qDetHhm

まずはNULLポインタについて

コンピュータがなにか作業するときにデータを置いておくスペースとして主記憶、いわゆるメモリがあるのはおそらくみんな知ってるよね
ポインタはこのメモリ上の番地(アドレス)を格納する変数で、アドレスとデータ型の情報をあわせてどこに何をしまったかを管理できる
しかし、まだ必要なデータが用意されてないなどの理由でポインタが「どこも指していない」状態のとき、それをどう表現するべきかという問題がある
CやC派生の言語では、ポインタがNULLという特殊な場所を指しているとき、そのポインタはNULLポインタと呼ばれ、これが「どこも指していない」状態を示すものとして使われる
ちなみに「NULLという特殊な場所」というけど実体はメモリの0番地で、ここはふつうユーザーがアクセスできない領域なのでこういう特殊状態を表すための値として白羽の矢が立ったというわけ

40以下、名無しにかわりましてVIPがお送りします:2024/07/21(日) 02:04:41.39 ID:8qDetHhm

ただ、今回は0x9c(156)という中途半端な、とはいっても0にかなり近いアドレスにアクセスしようとしてクラッシュしていて、これはクラスや構造体へのポインタがNULLポインタなのにもかかわらずそのメンバにアクセスしようとしたからじゃないかと推測されている

そもそもクラスや構造体のインスタンスの実体は、メンバ変数やらメソッドへのポインタやらを詰め合わせてメモリ上にごちゃっと載せたものなんだ
とは言っても無造作に置くわけじゃなく、コンパイラは各メンバに対してオフセット、つまり各メンバをインスタンスの先頭のアドレスから何番地先に配置するかを決めておく
これで、あるインスタンスのあるメンバにアクセスしたいときは、(先頭のアドレス) + (メンバのオフセット) で求まる番地を見に行けばいいとわかる
そして、クラスや構造体へのポインタ(例えば class MyClass に対する MyClass*)はインスタンスの先頭のアドレスを保持している
もしこれがNULLポインタだったら…?
NULLは0だから、NULLポインタからオフセットが156のメンバにアクセスしようとしたのだとすれば、そのまんま0x9c(156)という中途半端なアドレスにアクセスしようとして失敗することになる

41以下、名無しにかわりましてVIPがお送りします:2024/07/21(日) 02:16:17.47 ID:8qDetHhm

実際に試してみよう
Nectarクラスがあるとして、nectarインスタンスを作ってメンバvolumeの値を読み出したいとする

これは正しい例
Nectar* nectar = new Nectar(); でOSに Nectar のインスタンスをメモリ上に作ってもらい、その先頭アドレスを nectar に保存して、nectar->volume でメンバ volume の値を正常に読むことができている
https://wandbox.org/permlink/TdyKvuIERX0cWdkl

これはバグっている例
Nectar* nectar = NULL; で nectar をNULLポインタにして、nectar->volume でメンバ volume の値を読もうとすると、(先頭アドレス) + (メンバのオフセット) = 0 + 8 = 8 番地にアクセスしようとして、OSにそこ入っちゃダメ!って怒られて強制終了させられる
https://wandbox.org/permlink/WNyFLKif7ioptgQf

42以下、名無しにかわりましてVIPがお送りします:2024/07/21(日) 02:38:57.09 ID:8qDetHhm

では、今回の騒動ではなぜプログラムが強制終了するにとどまらずブルースクリーンを起こすまでに至ったのか?
それは、問題のプログラムがカーネルモードドライバだったからなのだ
https://learn.microsoft.com/ja-jp/windows-hardware/drivers/gettingstarted/user-mode-and-kernel-mode

普通のプログラムはユーザーモードという制限されたモードで動く
ユーザーモードではメモリやデバイスへのアクセスはOSやカーネルモードドライバに頼んでやってもらう必要がある
そうすることで他のプログラムが使っているメモリ空間を侵食するなどの危ない動きができないようにしている

対してカーネルモードはほぼ制限なくメモリやデバイスにアクセスできる
今回の騒動を引き起こした製品はセキュリティのために他のプログラムの動きを検知したり止めたりする必要があるからカーネルモードで動かないといけないんだね

ただ、カーネルモードとはいえ、0x9c(156)なんていうアドレスにアクセスしようとしたら、そこは恐らくOSや他の重要なプロセスが使っている領域なので、OSは破壊を止めるためにカーネルモードだとしてもプログラムを強制的にクラッシュさせる可能性が高い
そしてWindowsでは、カーネルモードドライバがクラッシュすると事態を悪化させないうちに安全にセーフモードに移行できるようにOSが自決してシステムを止めるようになっている
こうしてコンピュータはブルースクリーンを映し出して止まるんだね


このIDをNGリストに追加する

今後このIDの書き込みやスレッドを表示したくない場合、以下のボタンをクリックしてください。
NGリストに追加

このスレッドは過去ログです。