-
ARM64でのみ発生したクラッシュ
- EdgeDBのネットワークI/OコードをPythonからRustへ移植する過程で、ARM64のCIランナーでテストが断続的に失敗する問題が発生した。
- 当初はデッドロックのように見えたが、実際にはプロセスがクラッシュしており、テストランナーがそれを検知できていなかった。
-
初期の仮説
- ARM64でのみ問題が発生する理由を理解するため、メモリモデルの違いを考慮した。
- Intelのメモリモデルは厳格である一方、ARMはより弱いメモリモデルを持つ。
-
CIマシンでのデバッグ
- AWSでARM64ランナーに直接接続して問題を調査した。
- プロセスがクラッシュしてコアダンプを残しており、それを調べることで問題の原因を突き止めた。
-
実際の原因: setenvとgetenv
setenvはマルチスレッド環境で安全ではなく、getenvとの相互作用でクラッシュを引き起こす可能性がある。
- 環境変数の再割り当てが問題の原因であることが判明した。
-
openssl_probeとの関連
openssl-probeがSSL_CERT_FILEとSSL_CERT_DIR環境変数を設定する際に問題が発生した。
- Rustの
rust-native-tlsがこれらの環境変数を設定する過程でクラッシュが発生した。
-
ARM64 Linuxでのみ発生した理由
- クラッシュは複数の条件が重なった場合にのみ発生し、環境変数の数やI/O失敗などがその条件に含まれる。
-
解決策
reqwestのrust-native-tls/opensslバックエンドからrustlsへ移行することを決定した。
- Rustプロジェクトは環境設定関数をunsafeにする計画であり、glibcプロジェクトは
getenvのスレッド安全性を改善している。
4件のコメント
Setenv はスレッドセーフではなく、C はこれを修正したくない
setenv関数がまた問題を起こしています。私はタイトルを「C stdlibのスレッド非安全性は、安全だとされるRustでさえ救えない」と書くつもりです。 :)
確かに理解しました。
Hacker Newsの意見
Rustの次のエディションでは環境設定関数がunsafeになる予定で、競合するcrateに影響する可能性がある
set_varとremove_varは、2024 editionではunsafe {}ブロックの使用が必要になるglibc向けのパッチで
getenvはより安全になったが、Cでは依然として環境へ直接アクセスできるため、完全に安全ではないsetenvをマルチスレッド安全にすることに消極的だが、少なくとも新しいスレッドセーフAPIは定義されるべきだLinuxで環境まわりのバグに遭遇するのは、ある種の通過儀礼のように見なされている
getenv_r()を提供し、setenv()と同期させ、コンパイル時・リンク時に警告を出すようにしていれば、問題解決の助けになったはずだ環境変数を使った設定は"12-factor app"ムーブメントの一部だったが、これは愚かなやり方だと思う
Amazon AWS上で動くCIマシンは、実際のrootユーザーを使えるという利点がある
直感に反するバグを掘り下げた素晴らしい記事だ
env::set_varは今やunsafeになったset_varやremove_varを使わないことが唯一の安全な選択だあるコードベースで
setproctitleが動かなかった経験を思い出させるnumpyをインポートした後はsetproctitleが動作せず、これはnumpy初期化時のgetenvまたはsetenv呼び出しによってenvironのアドレスが変更されたためだ