2024.10.21
お互い疑心暗鬼になりがちな、経営企画と事業部の壁 組織に「分断」が生まれる要因と打開策
QEMUのバグを見つけてパッチを送った話(全1記事)
リンクをコピー
記事をブックマーク
だいみょーじん氏(以下、だいみょーじん):では、「QEMUのバグを見つけてパッチを送った話」をお話しします。今回の発表は、まず自己紹介をしてバグ発見の経緯をお話しして、その後にバグに関する考察と原因調査、そして修正パッチ、まとめという流れで発表をしていきます。
まずは自己紹介です。だいみょーじんと申します。自動車業界でエンジニアをやっていて、趣味はOS自作です。こんな感じのOSを作っています。最近のUEFI(Unified Extensible Firmware Interface)ではなく、古き良きレガシーBIOS(Basic Input Output System)で動くOSになっております。
今回のバグ発見の経緯です。自作OSにx87FPU、浮動小数点演算装置を用いた計算アプリを実装しました。(スライドを示して)こんな感じで簡単な四則演算や三角関数が一応計算できています。
ただ、ある状況で変なバグが起きたんですね。図のように、QEMUの上で自作OSが動いていて、OSがFPU(Floating Point Unit)、浮動小数点演算装置に向けてなにか演算命令を送って、FPUは演算結果を返して、OSがそれを出力をします。これは正しい値なんですね。ただ、GDBからQEMU上で動いているOSにアタッチしてFPUの演算結果をこのGDBから表示させるとなんか値がおかしい。
OSの出力は正しいのに、デバッガでFPUの中身を確かめるとおかしな値になっているという逆ハイゼンバグに遭遇しました。
(スライドを示して)これが実際のバグなんですが、ここらへんでfninit命令でFPUを初期化して、このあとFPUのスタックにいろいろな浮動小数の値をプッシュしています。GDBのinfo floatコマンドでFPUの中身を表示できるんですが、例えばこれはFPUにプッシュした値としてe+2165とか、とてつもない値が入っています。
あともう1つ、一番下にControl Wordというのがあります。ここも本来であれば0x037fになるはずなんですが、これもまったく違う値になっている。よく見ると変なところに怪しげな037fがある。これをよく覚えておいてください。こんな感じのバグに遭遇しました。
このバグに関する考察です。OS自体は正しい値を出せているので、OSやFPU自体は間違っていないと思うんですよ。となると、(スライドの)赤い線上のどこかで値がおかしくなっていると思うんですよね。このバグをどうやって調査しようかなと思ったんですが、とりあえずもう1つGDBを起動して、ここらへんで値がどう処理されているのかを追ってみようと思いました。
では調べていきましょう。「デバッグするぞー!」ということで、GDBのソースなんか読んだことはなかったのですが、気合いでmain関数から一歩ずつ潜っていくと、いろいろと見えてきました。
(スライドを示して)このstart_event_loopという関数の中に無限ループがあって、さらにその中にgdb_do_one_eventという関数があって、どうやらここでGDBに打ち込まれたコマンドを1つずつ処理しているっぽいです。
さらに潜っていくと、GDBのinfo floatコマンドでFPUの各レジスタの値を表示する関数を見つけました。この関数の中で出力される各FPUレジスタの値の出所をさらに辿っていくと、レジスタの溜まり場みたいなものがあったんですね。(スライドを示して)例えばこれは0202とあって、EFLAGSレジスタっぽいものとか、7c00のこれはEIPレジスタっぽいです。
ここの0000が、どうやらControl Wordとして出力されているようなんですが、ここにその本来のControl Wordの値である037f。怪しげな037fがここにあります。
さらにレジスタの溜まり場の出所を辿ると、どうやらgパケットなるものでQEMUからGDBに各レジスタの値が送られていることがわかりました。これがそのgパケットの中身です。文字列で送られているのですが、ちゃんとEIPもいるし、EFLAGSっぽいのもいるし、ここの0000がFPUのControl Wordとして表示されているんです。
だけどエンディアンでちょっと逆になっていますが、相変わらずこの怪しげな037f、本来のControl Wordの値っぽいものがあります。
さらに調査を進めると、QEMUはgパケットのデータ構造を定義しているXMLファイルをGDBに送っていて、GDBはそれに基づきgパケットから各レジスタの値を抽出しているということがわかりました。そのXMLをよく読んでみると、EFERレジスタの大きさに関して矛盾した記述がありました。(スライドの)上の赤いほうは、EFERレジスタの値のサイズが8byteと言っていて、下の緑のほうはEFERレジスタのbitsizeが32bit、つまり4byteであると言っています。
ここで1つの仮説が出てきます。QEMUとGDBの間で、EFERレジスタの大きさの合意が取れていないと仮定すると、辻褄が合います。(スライドを示して)この図ではgパケットがあり、上側はQEMUがどのようにgパケットを構築しているのかを表しています。それに対して下側は、GDBがgパケットを受け取って、それをどのような構造として解釈しているのかを表しています。
途中まで両者は一致しているんですが、EFERの大きさの合意が取れていないので、これ以降の全部のレジスタが4byteずれてしまっている。そのため、FPUのCTRL Wordレジスタに、ちゃんと037fと入れたのにGDBではそれがR7の一部分だと解釈されてしまって、それがその怪しげな037fになったんじゃないかと考えました。
仮説が立ったところで、EFERのレジスタは4byteであると修正して、もう一度実行してみると、こうなりました。先ほどと同じようにFPUをfninitで初期化して、先ほどと同じ数値をFPUスタックにプッシュします。info floatコマンドでFPUの中身を表示してみると、Control Wordはきちんと037fになりました。スタックの中にもまともな値が入っています。
ということでちゃんと直ったので、パッチを送って無事にマスターブランチにマージされました。これで「めでたしめでたし」といきたいところなんですが、本当に直ったのでしょうか? もう一度確認してみましょう。
まず、Control Wordはきちんと037fになっています。スタックの中にもまともな値が入っています。ですが、実は位置がずれています。FPUスタックは上から下に向けて伸びていく。要するにプッシュされた順番どおりに出力されるはずなので、実際には(スライドを示して)こういう順番にならないといけません。
これに関しては、バグの調査でソースコードを読んでいた段階で「ここおかしくないか?」と薄々気づいてはいました。これは何が起きているかというと、スタック内の要素の指定方法は2つあって、絶対位置指定のR0~R7というものと、スタックのトップからの相対位置指定であるST0~ST7というものがあります。
つまりこの相対指定は、プッシュしたりポップしたりするたびに、全体が上に行ったり下に行ったりする。現在のスタックのトップは、この矢印で示されたR1なので、そのRxやSTxがこう対応しているのですが、このRxやSTxがごちゃごちゃにされてずれているというのが起きています。
調べたところ、これもやはりQEMUとGDBの間のgパケットの解釈違いでした。Rxでgパケットに乗せて送信しているのに対して、受信したgパケットは相対位置指定で入っているものだとGDBが解釈してしまったために、そのずれが起きてしまっていました。
gパケットの仕様的には、どうやら相対位置指定で送るのが正しいということだったので、QEMUで適切な変換を施すことで、正しい順序でFPUの各要素を表示することができるようになりました。このパッチはまだマージされていませんが、そのうちマージされると思います。
最後にまとめです。OSを自作していたらGDBでQEMUをデバッグした時のみ値がバグるという逆ハイゼンバグに遭遇しました。調査したところ、x86アーキテクチャのFPUまわりのQEMUとGDBの連携が壊滅的にバグっていたので直しました。だいぶマシにはなりましたが、まだまだ細かい部分でバグり散らかしているので少しずつ直していこうと考えています。ご清聴ありがとうございました。
司会者:ありがとうございました。時間ピッタリでありがとうございます。「バグり散らかしている」(笑)。
(一同笑)
司会者:でも、defineがずれているのは「そっか」と思いましたけど、扱いが違うと気づくのがすごいなと思いました。きちんと把握していないと「わからない」で、直ったら「直った!」で終わっちゃいそうですね。
(一同笑)
司会者:そう思っていたら、最後のオチが「バグり散らかしている」で、ちょっとおもしろかったですね。(コメントを見て)「そうそう、QEMU意外とバグってるんだよ。A64FX(富岳CPU)のエミュレーションをさせていると、コーナーケースでいろいろと計算を間違うので困った。SVE512とかは世界に1つしかないから仕方ないといえばそれはそう」。「アーキテクチャ固有の話は、ユーザーが少ない、さらにデバッガを使っている人が少ない、誰も気づいている人いない」みたいな(笑)。
「ユーザーが少ないとバグりがち」。ちなみにこのバグり散らかしているのは、リストにしていたりしませんか?
だいみょーじん:リストは特に作っていませんが、(スライドを示して)例えばここは全部Validになっているとか。
司会者:確かに(笑)。
だいみょーじん:他にもいろいろありますね。
司会者:お気づきでしょうか?ここがすべてValidになっている……確かに(笑)。
(一同笑)
司会者:ありがとうございました。
2024.11.13
週3日働いて年収2,000万稼ぐ元印刷屋のおじさん 好きなことだけして楽に稼ぐ3つのパターン
2024.11.21
40代〜50代の管理職が「部下を承認する」のに苦戦するわけ 職場での「傷つき」をこじらせた世代に必要なこと
2024.11.20
成果が目立つ「攻めのタイプ」ばかり採用しがちな職場 「優秀な人材」を求める人がスルーしているもの
2024.11.20
「元エースの管理職」が若手営業を育てる時に陥りがちな罠 順調なチーム・苦戦するチームの違いから見る、育成のポイント
2024.11.11
自分の「本質的な才能」が見つかる一番簡単な質問 他者から「すごい」と思われても意外と気づかないのが才能
2023.03.21
民間宇宙開発で高まる「飛行機とロケットの衝突」の危機...どうやって回避する?
2024.11.18
20名の会社でGoogleの採用を真似するのはもったいない 人手不足の時代における「脱能力主義」のヒント
2024.11.19
がんばっているのに伸び悩む営業・成果を出す営業の違い 『無敗営業』著者が教える、つい陥りがちな「思い込み」の罠
2024.11.13
“退職者が出た時の会社の対応”を従業員は見ている 離職防止策の前に見つめ直したい、部下との向き合い方
2024.11.15
好きなことで起業、赤字を膨らませても引くに引けない理由 倒産リスクが一気に高まる、起業でありがちな失敗