アカウント名:
パスワード:
「スタックは上のアドレスから使われる」のが諸悪の根源。下から使ってりゃオーバーフローしても被害はその関数の自動変数で収まる。
スタックがどちらに成長するかは処理系(とアーキテクチャ)依存です。とあるフリーのOSのライブラリを読んでるときに「MIPSでは下から上に成長するよ!」と書いてありました。上から下に向って成長しなければならない必然性はもはやありません。でも、いざ切り替えるとなると、いろいろうまく動かなくなるところがでてきそうですね。
でも、切り替えたところでどのくらいメリットがあるでしょうか? overflowの攻撃を防ぐだけなら実質address space layout randomizationだけで100%防げているような感覚はあります。昨今バッファオーバーフローで攻撃が成立するのは明示的にaddress space layout randomizationをオフにしたプロセスだけではないでしょうか。
配列は上から下につかんだからしょうがねーべよ。
言語仕様でそこまで規定していましたっけ?単に昔のプロセッサはpush/popの方向が一種類しかなかったための実装上の問題と思ってましたが。
アイデアとしては面白いですが、その場合retaddrとデータ部はどこに置くことになりますか?
現状、retaddrはpushすることにより、autoの変数はスタックポインタを減ずることで実現しているわけですが、現状のCPUのSPが1つである以上、retaddrとauto変数を別の場所に置くための仕掛けが必要となります。この場合の実装として考えられるのは、1. 2つのSPコンテキストを維持してリターン直前にretaddr用のaddrをSPにロードする 2. auto 変数をヒープを用いるよう変更してしまう という方法あたりになるかと思いますが、1ならコンテキスト(退避したレジスタの内容)をどこに配置するのか(結局固定した場所に配置してたらセキュリティ上の優位性は無いのではないか?)とか、2なら決定的に速度が低下しそうという懸念があります。
親スレッドでは「いちいちヒープを使うよりも」と書いてあるので、1ということになりますかね。
今思い付いたアイデアですが、いっそ、SPを使わないコード(全部inline展開)を生成するようにすればいいのでは? そうして出来上がったものがCと言えるかどうかはすごく微妙ですが。(苦笑)
どんなものを想定されているのか、是非ここでお話いただければ刺激になります。
>現状のCPUのSPが1つである以上
現状の(x86以外の)CPUの多くはSPなんてなくて、単にC言語では汎用レジスタの一つをSPとして使ってるだけです。retaddrは別のレジスタに取っとくか、自分でスタックに積む。
いずれにせよもう一個汎用レジスタ使えば済む(x86はレジスタ少ないからアレだけど)。
それは知りませんでした。こんど注意深くコードを見てみようと思います。ありがとうございました。
forthなんか普通にデータスタックとコードスタックが独立してるしな。
ハードウェアの問題なら普通にユーザ用とシステム用でスタック2本持ってるし。
より多くのコメントがこの議論にあるかもしれませんが、JavaScriptが有効ではない環境を使用している場合、クラシックなコメントシステム(D1)に設定を変更する必要があります。
UNIXはシンプルである。必要なのはそのシンプルさを理解する素質だけである -- Dennis Ritchie
分岐とデータのスタックを分離していないこと (スコア:1, 興味深い)
auto変数の配列を(リターンアドレス等に使う)メインのスタックではなく、別のスタックに置けば
かなりの改善を得られると思うんです。
別のスタックに対する操作はコンパイラが生成したコードでやるわけですから透過的に行えるし、
そのオーバーヘッドは、いちいちヒープを使うよりも遥かに少なくそしてメモリリークのバグをやらかしにくい。
まぁリターンアドレスを細工されることは防げても、他のデータにはみ出して上書きして特権的なフラグを操作する・・・なんてことには対処できませんが。
Re:分岐とデータのスタックを分離していないこと (スコア:2, すばらしい洞察)
「スタックは上のアドレスから使われる」のが諸悪の根源。
下から使ってりゃオーバーフローしても被害はその関数の自動変数で収まる。
Re:分岐とデータのスタックを分離していないこと (スコア:3, 参考になる)
スタックがどちらに成長するかは処理系(とアーキテクチャ)依存です。とあるフリーのOSのライブラリを読んでるときに「MIPSでは下から上に成長するよ!」と書いてありました。上から下に向って成長しなければならない必然性はもはやありません。
でも、いざ切り替えるとなると、いろいろうまく動かなくなるところがでてきそうですね。
でも、切り替えたところでどのくらいメリットがあるでしょうか? overflowの攻撃を防ぐだけなら実質address space layout randomizationだけで100%防げているような感覚はあります。昨今バッファオーバーフローで攻撃が成立するのは明示的にaddress space layout randomizationをオフにしたプロセスだけではないでしょうか。
Re: (スコア:0)
配列は上から下につかんだからしょうがねーべよ。
Re: (スコア:0)
Re: (スコア:0)
コードとスタックの領域を別々に確保できたわけじゃないので、コードが下から使われる以上、スタックは上からというのが必然だったのじゃよ。
Re: (スコア:0)
言語仕様でそこまで規定していましたっけ?
単に昔のプロセッサはpush/popの方向が一種類しかなかったための実装上の問題と思ってましたが。
Re: (スコア:0)
バッファ・オーバーフロー攻撃を防ぐ“万能薬”はあるか? [nikkeibp.co.jp]
Re:分岐とデータのスタックを分離していないこと (スコア:2)
アイデアとしては面白いですが、その場合retaddrとデータ部はどこに置くことになりますか?
現状、retaddrはpushすることにより、autoの変数はスタックポインタを減ずることで実現しているわけですが、現状のCPUのSPが1つである以上、retaddrとauto変数を別の場所に置くための仕掛けが必要となります。
この場合の実装として考えられるのは、1. 2つのSPコンテキストを維持してリターン直前にretaddr用のaddrをSPにロードする 2. auto 変数をヒープを用いるよう変更してしまう という方法あたりになるかと思いますが、1ならコンテキスト(退避したレジスタの内容)をどこに配置するのか(結局固定した場所に配置してたらセキュリティ上の優位性は無いのではないか?)とか、2なら決定的に速度が低下しそうという懸念があります。
親スレッドでは「いちいちヒープを使うよりも」と書いてあるので、1ということになりますかね。
今思い付いたアイデアですが、いっそ、SPを使わないコード(全部inline展開)を生成するようにすればいいのでは? そうして出来上がったものがCと言えるかどうかはすごく微妙ですが。(苦笑)
どんなものを想定されているのか、是非ここでお話いただければ刺激になります。
Re:分岐とデータのスタックを分離していないこと (スコア:1)
>現状のCPUのSPが1つである以上
現状の(x86以外の)CPUの多くはSPなんてなくて、単にC言語では汎用レジスタの一つをSPとして使ってるだけです。
retaddrは別のレジスタに取っとくか、自分でスタックに積む。
いずれにせよもう一個汎用レジスタ使えば済む(x86はレジスタ少ないからアレだけど)。
Re: (スコア:0)
CPUが管理するもの・・・x86ならpush/pop/call/retなどで使うもの
ソフトウェアが管理するもの・・・いくらでも実装例ありますね
2つがあるわけです。
レジスタに入らないようなサイズのauto変数(つまり配列や構造体ですね)を、CPU管理のスタックではなく、ソフトウェア(この場合はコンパイラが生成したコードによる)管理のスタックに置くことは、レジスタウィンドウを持つCPU向けのCコンパイラでは、普通に行われていることですよ。
Re:分岐とデータのスタックを分離していないこと (スコア:1)
それは知りませんでした。こんど注意深くコードを見てみようと思います。
ありがとうございました。
Re: (スコア:0)
forthなんか普通にデータスタックとコードスタックが独立してるしな。
ハードウェアの問題なら普通にユーザ用とシステム用でスタック2本持ってるし。