PyTorchのバックワードパス:ゼロ損失項に隠されたチューリング完全なマルウェア
研究者らは、浮動小数点精度の癖を利用することで、損失指標がゼロを報告していても、PyTorchの勾配降下法内で任意のコード実行が可能になることを実証しました。
英語の原文から自動翻訳されました。
新しい技術的なデモンストレーションにより、PyTorchの自動微分エンジンが訓練時のバックワードパス中に任意のプログラムを実行するように仕組めることが明らかになりました。IEEE 754浮動小数点演算と記号計算の間の不一致を悪用することで、攻撃者はモデルの重みの中にConway's Game of Lifeなどのチューリング完全なステートマシンを組み込むことができます。この際、報告される損失値は正確にゼロのままとなり、標準的な監視や検証チェックを回避します。
何が起きたか
この発見は、PyTorch内の浮動小数点精度の異常に関する調査から始まりました。研究者は当初、数学的に損失関数がゼロと評価されながら、勾配降下法中に重みの更新を引き起こす可能性があるかどうかを調べようとしていました。結果は、すべての訓練メトリクスで 0.0 と記録されながら、オプティマイザーに複雑なロジックを実行させるような損失項を構築できることを確認しました。この発見は、理論上の数値的な好奇心を、機械学習パイプラインに対する現実的なサプライチェーン攻撃ベクトルへと変えます。
脅威モデルには、MetaのLlama-3-8Bなどのオープンソースモデルをファインチューニングする開発者が含まれます。標準的なセキュリティ対策としては、チェックポイントファイルのSHA-256ハッシュの検証や、明白なバックドアやシステムコールがないかのPythonコードレビューなどが挙げられます。訓練曲線が正常に見え、補助的な損失が平坦であれば、環境は通常安全だと判断されます。しかし、攻撃者は UltraLoss という偽の正則化器のような悪意のあるパッケージを配布し、ベースとなるチェックポイント、アーキテクチャ、データセットを変更することなく、訓練ループにシームレスに統合させることができます。
この悪意ある損失項が総損失に追加されると、各イテレーションで 0.000000 を報告します。この無害に見える外見にもかかわらず、total_loss.backward() と optimizer.step() の呼び出しにより、モデルの重みに埋め込まれた任意のプログラムが実行されます。攻撃者はフォワードパス中にパラメータを直接変更する必要がないため、標準的な静的解析や損失値のランタイムログによる検出が困難になります。
仕組み
このエクスプロイトは、ハードウェアレベルの浮動小数点切り捨てと、autodiffエンジンで使用される記号微分のルールとの間の不一致に依存しています。コアメカニズムは、関数 h(u) = (2^30 + u) - 2^30 を使用します。IEEE 754 float32 演算では、2^30 に小さな値 u(ここで |u| < 64)を加えると、その大きさにおける表現可能な数値の間隔が128であるため、2^30 に丸め戻されます。その後 2^30 を引くと 0.0 が得られます。しかし、PyTorchのautogradエンジンは加算と減算を記号的に扱い、それらの導関数を1として記録します。したがって、フォワードパスではゼロが見えますが、バックワードパスでは勾配1が計算されます。
ステルス性の高い勾配ソースを作成するために、研究者はこの挙動を積の規則(product rule)と組み合わせています。スカラー損失は、2つの項の積によって構成されます。1つはバックワードパスでターゲットの勾配ベクトルとして評価されますが、フォワードパスではゼロです。もう1つは両方のパスでゼロですが、導関数は1です。具体的には、損失は (v - h(v)) * h(theta) の合計として定義されます。ここで v は望ましい重み更新です。フォワードパスでは h(theta) がゼロなので、損失はゼロです。バックワードパスでは、積の規則により、最初の項の保存されたフォワード値が勾配として流れることで、事実上 theta.grad を望ましい更新ベクトルに設定します。
このメカニズムにより、オプティマイザーは各訓練ステップ中に、Conway's Game of Lifeなどのセルオートマトンの次の状態へ重みを上書きすることができます。研究者はこれを、MNISTで訓練されたCNNの1,024個の重みにGame of Lifeのルールを配線することで実証しました。2,345ステップにわたって、補助損失はゼロのまま保たれ、モデルは通常の精度を達成し、重みはエラーなくオートマトンをシミュレートすることに成功しました。更新値は小さい整数(-1, 0, 1)であり、float32 では丸め誤差が発生しないため、正確な状態遷移が保証されます。
主要な詳細
- このエクスプロイトが機能するのは、IEEE 754
float32が大きな定数への小さな加算をゼロに丸める一方で、PyTorch autogradは導関数を記号的に1として計算するためです。 - フォワードパスでは
0.0と評価されながら、バックワードパスでは任意の勾配ベクトルを生み出すような損失関数を構築できます。 - この攻撃により、標準的なSGD更新中に、Conway's Game of Lifeなどのチューリング完全なプログラムがモデルの重み内で実行可能になります。
- 「math-washing」と呼ばれる手法を用いて、セルオートマトンのルールを幾何学的な正則化器や畳み込み層のように見せかけることで、悪意あるロジックを隠蔽できます。
- ステートマシンのカーネル重みなどの重要な定数を、ハイパーパラメータ調整の結果であると主張する外部設定ファイルに格納することが可能です。
- コンパイルされたCUDA拡張機能を使用することで、ロジックをさらに不明瞭にできます。悪意あるバイナリコードを、標準的なスペクトルペナルティのように見える薄いPythonクラスでラップします。
なぜ重要なのか
この脆弱性は、損失メトリクスの監視とファイルハッシュの確認だけでML訓練パイプラインのセキュリティを確保できるという前提を覆します。開発者はしばしば、数学的な実装を深く検査することなく、オープンソースライブラリや正則化技術を信頼します。ライブラリが収束性や安定性の向上を謳う場合、その内部動作が非標準的な勾配挙動に対して監査されることは稀です。このエクスプロイトは、出力メトリクス上では完全に正常に振る舞いながら、訓練プロセスそのものを乗っ取ることが可能なライブラリの存在を示しています。
AI製品を開発しているチームにとって、これはサプライチェーンセキュリティにおける盲点を浮き彫りにします。従来のソフトウェアセキュリティはコード実行とデータアクセスに焦点を当てていますが、ML訓練では最適化アルゴリズム自体がペイロードキャリアとなるという新たなベクトルを導入します。悪意あるコードは勾配降下法の正当な数学的演算内で実行されるため、多くの従来のセーフガードを回避します。これにより、エンジニアがサードパーティのMLコンポーネントを検証する方法を変革する必要があります。表面レベルのメトリクスを超え、損失関数の数学的な整合性を確認する方向へ移行しなければなりません。
対応策
- 大きな定数と小さなオフセットを含む操作があるかを確認し、カスタム損失関数や正則化器を監査してください。これらは浮動小数点悪用の兆候かもしれません。
- 損失値だけに頼るのではなく、バックプロパゲーション中に計算された勾配が期待される数学的微分と一致しているかを確認してください。
- MLライブラリによって読み込まれる設定ファイルや外部アセットを検査し、文書化された目的と一致しない説明のつかない定数やカーネルがないか確認してください。
- 信頼できるソースからのコンパイル済み拡張機能の使用を優先し、その整合性を検証してください。これらの拡張機能はPythonコードよりも効果的に悪意あるロジックを隠蔽できる可能性があります。
- 損失が安定していても異常なパターンを検知できるよう、重み更新を期待されるノルムと比較するランタイムチェックを実装してください。
- サードパーティのMLパッケージを、システムレベルの依存関係と同じ厳密さで扱い、それらが訓練中に任意のロジックを実行しうることを認識してください。



