Symbol GC - @nari3
symbolとは?
- primitiveなデータ
symbolの落とし穴
脆弱性の例
- RailsでDigestのヘッダ情報をhashに変換していた。whitelistにも登録されていないので、たくさんのkeyを指定するとメモリ使用量が膨大になっていく
- @nari3さんが笹田さんのアイデアを実装
- 「これで2.1をオワコンにしてしまった。またやってしまった」w
やっている言語、やっていない言語がある
- 実装に依存する
- 言語仕様に結構かかれていない
他の言語実装
Ruby
"sym".to_sym
で、frozen stringにしkeyを登録する- "sym"というシンボルのidは1001。ID2SYM(ID)関数でシンボルを取得出来る
- ID: シンボルを表す一意の値
- 比較がIDの比較だけなので、高速にできる
- SYMBOL(value)はGCの対象にならない
なぜGCの対象にならないのか?
シンボルのIDをC拡張のstatic libに格納するケース
- シンボルがGCの対象になると、sym_id(hash)も削除されちゃう
- 新しく同じシンボルが登録されると、IDが変わってしまう。そうするとメソッド呼び出しができなくなったり、色々な不具合がでる
一貫性が保てない問題
- Ruby側で死んでしまったシンボルも
どうやってシンボルGCを作るのか?
- Immotal symbol(C lang)とMortal Symbol(Ruby)の両方に分けてしまう
Immortal Symbol
- 対応するIDを持っているシンボル
- Cレベル。生き続けるシンボル
- Immortal symbol から mortal symbol への移行は無い
def bar; end
- RubyオブジェクトからIDを利用する。今と同じ
Mortal Symbol
IDを持たない。Rubyレベルで使われるシンボル
- 途中で移行するケースが有る
"bar".to_sym
- sym_id(hash)に、メモリのアドレスが格納される
- 不要になった場合はsym_id(hash)から削除される
mortal symbol を作った時に 同名の immortal symbol があった場合
- 既にあることを検知したら、そっちの方を使う
mortal symbol から immortal symbol に移行するケース
define_method("foo".to_sym){}
- mortal symbol だが、開放すると問題になるので、immortal symbolとしてピン止めする
落とし穴
全てのシンボルがGCの対象になるわけではない。immortal symbolには注意する
- mortalからimmortalに移行するケースがある
ライブラリの実装でダメな具体例としては、
rb_id2str(SYM2ID(sym))
を使っているケース。新しいAPIを使って欲しい- もし見つけたら、ライブラリ作者とかに報告して欲しい
まとめ
質問
- Q: 他にimmortal symbolになるケースがある?
- A: あまりruby書かないので思いつかないw