2014-07-27

Linus Torvalds、 GCC 4.9.0のコード生成にブチ切れる

Phoronixで知ったが、Linus TorvaldsがGCC 4.9.0のコード生成にブチ切れている。

問題はLinuxカーネルのload_balance()がランダムにパニックを起こすというもので、その原因は、報告者の使っているコンパイラーであるGCC 4.9.0のコード生成がおかしかったという話だ。

Linus様は御自ら生成されたコードを読み給い、平生と変わらぬ調子で物事の道理を示された。

Linux-Kernel Archive: Re: Random panic in load_balance() with 3.16-rc

From: Linus Torvalds
Date: Thu Jul 24 2014 - 14:47:25 EST

On Wed, Jul 23, 2014 at 6:43 PM, Michel DÃnzer <michel@xxxxxxxxxxx> wrote:

>> マイケル、
>> make kernel/sched/fair.s
>> やった結果のファイルを送ってくれないか?
>
> はいよ。gzipした。これでいいといいんだけど
> ところで、俺のツリーは3.16-rc6だ。

オッケー、コード生成みてるんだが、お前のコンパイラはマジで最高にクソだな。

Jakubをccに追加。gcc-4.9.0は致命的にぶっ壊れてるみたいだぜ。

見ろよコレ。お前のコンパイラーはなんか完全にトチ狂ったもの吐いてるぜ。定数とかな。マジで、このコンパイラーは幼稚園を卒園するのすら認められるべきじゃねーだろオイ。マジで「赤ん坊んときに落っこちて頭を打ったナマケモノ」レベルのアホレベルだぜ、コレは。

movq $load_balance_mask, -136(%rbp) #, %sfp
subq $184, %rsp #,
movq (%rdx), %rax # sd_22(D)->parent, sd_parent
movl %edi, -144(%rbp) # this_cpu, %sfp
movl %ecx, -140(%rbp) # idle, %sfp
movq %r8, -200(%rbp) # continue_balancing, %sfp
movq %rax, -184(%rbp) # sd_parent, %sfp
movq -136(%rbp), %rax # %sfp, tcp_ptr__
#APP
add %gs:this_cpu_off, %rax # this_cpu_off, tcp_ptr__
#NO_APP

定数の-136(%rbp)を見ろよ。マジかよ。コンパイラーが吐いてるのは 即 値 だぜ。

誰かGCCにバグ報告しろよ。こりゃマジでぶっ飛んだクソだからな。

だが、定数を吐くのは「バカすぎて耐えられん」程度のことでしかない。本当のバグはこれだ。

movq $load_balance_mask, -136(%rbp) #, %sfp
subq $184, %rsp #,

gccはスタックフレームを作ってるんだが、すでにスタックフレームの奥底に定数を保存した後でやってやがる。

x86-64 ABIはスタックポインターの後に128バイトのレッドゾーンを規定してて、これはその規程の元ならばセーフだ。どうもよくない気がするが(136 > 128)、だが問題は、フレームポインターを読み込むために、"pushq"を4つ%rspを更新するために使っているので、これはレッドゾーンギリギリのかろうじてセーフだ。

しかし、カーネルは-mno-red-zoneでビルドされている。ここではx86-64 ABIのレッドゾーンには従わない。というのも、カーネルモードでは割り込み不可能だし、スタックはレッドゾーンなしで使う。だから、"-mno-red-zone"は「推奨事項」なんかじゃなくて、カーネルに絶対必要な要件なんだよ。にもかかわらずgcc-4.9はバグだらけの一本グソなので無視しやがる。お前がこのバグにぶち当たった理由は、たまたま一命令を実行中に都合よく割り込みにひっかかったからだ(もしくは、似たようなことがすでに何度か起きていて、カーネルのデータ構造をすでにぶっ壊したか)

さて、俺が思うに、このレッドゾーンバグと、GCCがアホすぎて定数を吐くバグは、なんか関係していると思うね。なんか生存解析(liveness analysis)かなにかで、スタックデクリメントをいつ挿入するのかを決定しているが、定数には生存もクソもあったものじゃねーから無視されるとかさ。つまり二つのバグ(「アホみたいな定数吐くバグ」と「レッドゾーンスタックの不正な利用」)がうまく組み合わさったんじゃないかな。よくわかんねーけど。

とにかく、これはカーネルのバグじゃない。お前のコンパイラーが完全にぶっ壊れたコードを生成してる。gcc-4.9.0で誰かがコンパイルしないように警告すべきだし、Debianの奴らはピカピカの新しいコンパイラーをダウングレードするだろうよ。

Jakub、どう思う?

Linus

そして、Linus様は御自らGCCにバグ報告なされた。

Bug 61904 – Incorrect stack red-zoning on x86-64 code generation

Linus Torvalds 2014-07-25 19:01:36 UTC

(In reply to Andrew Pinski from comment #9)

> 問題のあるコンパイラーのバージョンは4.5.0(debug_insnが使われた時)から4.8.3までと、4.9.0と4.9.1だ。

なるほど、問題のあるコンパイラーのバージョンを避ける方法はないわけだ。4.9.0/1には警告を出せるだろうけどな。まだあまり一般的じゃないから。

やれやれ、コンパイル後にテストパスを追加する提案があるんだよ。だが、今のところ、手のひらをかざす(objdump + perl-script)程度のことだ。あまりイケてない。

問題は、こういうのはめちゃくちゃデバッグしにくいということだ。絶対ありえないはずのkernel oopsや破損を引き起こす。今回の問題はたまたまある二人にとって再現性があって特定箇所だったというだけの話だ。他にもあるのか? それを知る方法はない。

とにかく、素早く対応してくれてありがとよ。ただこの分だと既存のコンパイラーもちょっと心配になってきたんだがな。

さすがはLinus様だ。

3 comments:

Anonymous said...

バグの内容自体は深く見てませんけど原文のspillingていうのはここではregister spillingのことでは?

Anonymous said...

二点ほど、誤訳のせいでかなり意味不明になってしまっている点があったので指摘させていただきます。
・原文は Note the contents of -136(%rbp). なので「定数の-136(%rbp)を見ろよ。」ではなく「-136(%rbp)の中身を見ろよ。」だと思います。
・原文は We do *not* follow the
x86-64 ABI wrt redzoning, because we *cannot*: interrupts while in kernel mode *will* use the stack without a redzone. なので「ここではx86-64 ABIのレッドゾーンには従わない。というのも、カーネルモードでは割り込み不可能だし、スタックはレッドゾーンなしで使う。」ではなくて「ここではx86-64 ABIのレッドゾーンには従わない、なぜなら従えないからだ。カーネルモードでおきた割り込みはレッドゾーンなしでスタックを使う」といったところでしょうか(口調の雰囲気までは訳せてませんが…^^;)。

江添亮 said...

一点目。なるほど。
二点目。コロンを考慮していなかった。