Ruby バージョンチェックへ移行し Bug #22023 検知ロジックを簡素化
Bootsnap は Bug #22023 の有無判定に用いていた canary ファイルの実体評価を廃止し、Ruby のバージョン文字列だけで判定できるよう変更しました。これにより起動時のファイルロードが減り、判定ロジックが決定的かつ軽量になります。
背景
Bug #22023 は Ruby パーサが特定パターンを誤処理する問題で、過去は ruby_bug_22023_canary.rb をコンパイルさせ SyntaxError が発生するかで検出していました。canary 手法は Ruby のビルドやパッチレベルに依存し、同一バージョンでも結果が不安定になるケースが報告されていました。Ruby 4.0.4 で同バグは修正され、3.4 系でも 3.4.10 にバックポートされることが確定したため、バージョン範囲での判定が実現可能となりました。
技術的な変更
判定ロジックの置き換え
has_ruby_bug_22023 は now canary のコンパイルから RUBY_VERSION の正規表現マッチへ 置き換えられました。case RUBY_VERSION 文で対象バージョンを列挙し、該当する場合は true、それ以外は false を返すシンプルな実装です。
変更前:
has_ruby_bug_22023 = if defined?(RubyVM::InstructionSequence) && RubyVM::InstructionSequence.respond_to?(:compile_file_prism)
begin
RubyVM::InstructionSequence.compile_file(File.expand_path("../ruby_bug_22023_canary.rb", __FILE__))
false
rescue SyntaxError
true
end
end
変更後:
has_ruby_bug_22023 = case RUBY_VERSION
when /^3\.3\./
true
when /^3\.4\.(\d+)/
$1.to_i < 10
when /^4\.0\.(\d+)/
$1.to_i < 4
else
false
end
canary ファイルの内容変更
ruby_bug_22023_canary.rb はファイル自体は残りますが、実装コードがすべて削除され _ = "test" のみが残っています。これによりコンパイル時にバグを誘発する構文はなくなり、実行時の副作用がなくなります。
RuboCop 設定の追加
.rubocop.yml に以下の設定が追記され、削除された canary の残骸が RuboCop の対象外になるよう調整しました。
Layout/CaseIndentation:
EnforcedStyle: end
Style/PerlBackrefs:
Enabled: false
設計判断
バージョンチェックへの一本化 が本変更の中心的な設計判断です。Ruby のバージョンはビルド時に確定しており、外部ファイルの評価に依存しないことで判定の再現性が向上しました。トレードオフとしては、将来的に同様のバグが新バージョンで再び発生した場合に case 文を更新する必要がありますが、ロジックは単一の式に集約されているためメンテナンスコストは低く抑えられます。インターフェース名 has_ruby_bug_22023 は変更していないため、他コンポーネントへの破壊的影響はありません。
まとめ
Bootsnap は Bug #22023 の検出手段を canary 依存から Ruby バージョン判定へ置き換えることで、起動時オーバーヘッドを削減しつつ判定の確実性を高めました。canary ファイルは内容が削除され、RuboCop 設定も併せて整理されたため、コードベースの整合性が保たれています。