1を書いても0が読める!?
隠れた重要命令INVLPG
ゆるバグ2
@uchan_nos 2020年5月6日
先週のゆるバグでバグを発表した
 自作OS「MikanOS」の一部のアプリが実機GPD MicroPCで動かない
 調査を進めると
 あるメモリ領域に1を書いたはずなのに読むと0
 1を2回書くと1が読める
 などの謎挙動が観測された
fprintfでgrepが死ぬ
extern "C" void main(int argc, char** argv) {
// コメントアウトすると死なない
//DumpMem(reinterpret_cast<uint64_t>(_impure_ptr) + offsetof(_reent, __sdidinit), 2);
//fprintf(stderr, "hoge¥n");
//DumpMem(reinterpret_cast<uint64_t>(_impure_ptr) + offsetof(_reent, __sdidinit), 2);
// "argv[" を表示した時点で死んでしまう
for (int i = 0; i < argc; ++i) {
fprintf(stderr, "argv[%d] = %s¥n", i, argv[i]);
}
fprintf(stderr, "----¥n");
原因判明
 MikanOSのアプリはまずROでマップされる
 .text,.data,.bss
 初めての書き込みでCoW
 最終的に.textだけは各プロセスで共通になる
 CoWしたときにINVLPGしてなかった
仮想ページ
物理フレーム
物理フレーム
RO
RW
コピー
INVLPG
 TLBエントリを無効化(invalidate)する
 TLB???
TLB
 1回のメモリアクセスをするために
リニアアドレス→物理アドレス 変換が必要
 毎回階層ページング構造を辿るのは高コスト
 64ビットモードでは4レベル:PML4,PDP,PD,PT
 メモリアクセスが5倍になってしまう…
 変換結果をTLBにキャッシュして高速化!
ページ番号
物理アドレス
アクセス権
フラグ
TLBエントリ
INVLPGを忘れると…
 INVLPGを忘れると,
 メモリに1を書いても0が読める,
 というような不整合が発生する
仮想ページ
mem: 0
mem: 0
RO
RW
mov [mem], 1
; CoW
mov rax, [mem]
; rax == 0
実機ではどっちも0のまま…

1を書いても0が読める!?隠れた重要命令INVLPG