世界を動かす技術を、日本語で。

Bun Rustの書き換え: 「コードベースが基本的なMiriチェックに失敗し、安全なRustで未定義動作を許可する」

概要

  • Rustプログラムで 未定義動作 発生
  • from_raw_parts によるダングリング参照
  • Box の解放後にメモリアクセス
  • 問題箇所: PathString::slice
  • 安全なコード設計の重要性

Rustにおける未定義動作と危険なパターン

  • from_raw_parts は生ポインタからスライスを生成
  • Box::new で確保したメモリを drop で解放
  • 解放後のポインタを使い続けると ダングリング参照 発生
  • これはRustでも 未定義動作 (Undefined Behavior, UB)に該当
  • UBが発生すると、予測不能な動作やクラッシュ、データ破損の危険

問題の具体的なコードパターン

  • main 関数内でBoxの所有権を持つ変数を生成
  • その参照やポインタを PathString::init に渡す
  • drop でBoxのメモリを解放
  • 解放後に init.slice() で元のメモリへアクセス
  • これにより dangling pointer (ダングリングポインタ)が生じる

安全なRustコード設計のポイント

  • 所有権ライフタイム を明確に管理
  • メモリ解放後のポインタや参照の使用禁止
  • 生ポインタ操作や unsafe ブロックは最小限に留める
  • from_raw_parts の利用は、メモリの有効期間が保証されている場合のみ
  • &[u8]String など安全な型でデータ管理

修正案と推奨パターン

  • BoxVec など、所有権とライフタイムが自動管理される型を活用

  • 構造体に &[u8] を保持する場合、元データの所有権またはライフタイムも合わせて管理

  • どうしても生ポインタを使う必要がある場合は、 drop やスコープに注意

  • 例: PathString にBox自体を持たせる設計に変更

    • 例:
      struct PathString(Box<[u8]>);
      impl PathString {
        fn slice(&self) -> &[u8] { &self.0 }
      }
      

Rustで安全なコードを書くための心得

  • 未定義動作 を防ぐため、所有権とライフタイムの正しい理解
  • unsafe の使用は最小限、かつ十分な検証と解説コメントを添付
  • コンパイラやClippyの警告を無視しない
  • コードレビューやペアプログラミングで安全性を確保
  • Rustの 安全性設計思想 を守ることが品質向上への近道

まとめ

  • Rustは 安全性 が大きな特徴
  • しかし unsafe や生ポインタ乱用で危険なバグ発生
  • 所有権ライフタイム を守ることが最重要
  • 安全なRust設計を心がけることが、バグや未定義動作の防止につながる

Hackerたちの意見

注意やメディアについての考え方が大きく変わった本があるんだ。内容はあんまり良くないけど、ここで重要なことを指摘してる。派手な発表(ここでは、bunがメモリ安全なRustで数週間で書き直された)と、訂正の影響力には大きな非対称性があるんだよね。訂正は、古い記事の脚注に過ぎないことが多いし(ここではGHの問題)、この非対称性はマーケティングやPRのプロたちにしっかり理解されてて、積極的に利用されてる。

マーケティングやPRだけじゃなくて、メディアも知ってるよ。デタラメを流して後で訂正することで、元の記事や見出しを覚えている人が多いから、影響が残るってことを。

これって「嘘は真実が靴を履く前に世界の半分を回ることができる」っていう言葉に関連してる概念なのかな?

派手な発表(ここでは、bunがメモリ安全なRustで数週間で書き直された) 彼らは「メモリ安全」って主張してたの?この話題に関する議論では、彼らの雰囲気のあるコードベースが監査されていないunsafeブロックでパンパンになってるって指摘するコメントが何十もあったし、Rustを理解してないだけじゃなくて、そもそもプログラミング言語を理解する必要があるって考えに激怒してる人たちが軽くレビューしてるみたいだ。

うーん、今回の一般的な雰囲気を考えると、コードに対する批判を見つけては大きく取り上げようとする人が多い気がする。今のところ、ほとんどが浅い印象だけどね(大規模なLLM支援のポートを統合するのは、確かに、えっと、_大胆_な動きだとは言えるけど、実際の結果については、他の進行中のポートより悪いとは思えないし、見つかった問題についてはかなり騒がれているね)。

逆の問題を指摘すると思ってたんだけどね。「大々的な発表」がないのは、ポートがまだ進行中だから。完成してないし、リリースもされてない。見かける大々的な発表は、進行中のコードに対するドライブバイダンクの試みと、彼らが完成したとか完璧だと言ったかのようにほのめかす試みだけ。リライトは、スタート地点としてのコード翻訳だったんだ。>「大々的な発表(ここでは、bunが数週間でメモリ安全なrustに書き直された)」と、相対的に小さな訂正の範囲(古い記事の脚注やGHの問題など)。Bunチームは、コードがメモリ安全になったと大々的に発表したことはない。これはスタート地点だって明言してる。すぐに完璧になって、元のZigコードのメモリ問題が全部解決されると思ってる人は、Bunチームが言ったことじゃなくて、自分が想像した発表と争ってるだけだよ。誰かこのコードを元のコードベースに戻して、メモリ問題が存在するか確認した?

こういうエラーは予想通りだったよ。書き直しに対する問題とは思わないしね。彼らは、安定性が必要な人のためにZigの安定版を残してるし、最終的にはエラーも修正されるだろう。

こういうエラーは完全に避けられたはずだよ。Rustのエコシステムには、こういうエラーを検出するための有名なツールがあるし、ツールはunsafeブロックのミスによる全てのUBを検出できるわけじゃないけど、使うのは良いプラクティスとされてる。

このBunの書き直し、なんかマーケティングのスタントみたいに感じる。

やっと誰かがこのポイントを理解してくれた。

ただの過大評価で、がっかりってこと?

  • 無限トークンに何ドル使ったか神のみぞ知るけど、リライトをする * 「Claude CodeがBunチームに100万行以上のZigをRustに書き換えさせた」って大騒ぎして、ブログ記事を書く、VCたちはよだれを垂らす * 基本的なチェックが失敗 * Mythosにコードベースをボロボロにさせて、さらに神のみぞ知るくらいお金を使う * 別のブログ記事を書く * 詐欺師たちと頭の悪い連中が拍手して「妄想的な反AIの群れ」に対抗する * VCたちはさらに興奮する 拍手、拍手、拍手。これが金を稼ぐ方法だ、みんな。ところで、今すぐソフトウェアエンジニアを排除する必要がある。

BunチームやAnthropicの誰も、これをよりメモリ安全な言語にスワップする以外のことをマーケティングするためにひどいことをしたわけじゃない。これまでのところ、ほとんどの話題やマーケティングはAIに反対する人たちからの完全にネガティブなものだった。私の見解では、ほとんどの話題は、最近の彼らの決定に対するAnthropic自身への否定的な意見にも結びついていると思う。

これはただの企業の rug pull だと思う。Anthropic のニーズが唯一の優先事項で、他はどうでもいいって感じ。

これは驚くべきことではないね。彼らが促した直訳を考えると。最初に強い型システムの言語にBunを移行させて、その後でその型システムを利用して改善を進める方がいいんじゃないかな?最初のステップで完璧を求めるよりは、そっちの方が良さそうだよね。

ほとんどストレートな翻訳が(部分的に安全でない)Rustに展開されるのは驚くことじゃないよね。ちょっと残念なのは、Rustのコードに「unsafe」とマークされていないAPIがあって、それでもUBを引き起こす可能性があること。こういう翻訳をする時は、常に慎重に行動して、最初からすべて/ほとんどを「unsafe」とマークするようにしてるよ。スロップボットにも同じことをさせるべきだと思う。そしたら、個々の部分の安全性を一つずつ確認できるから。

そうだね、今はmiriみたいなツールでリライトにバックプレッシャーをかけて、Claude Codeが自動的に改善できるのは明らかだね。

プロンプトを「未定義の動作がないことを確認する」って更新すれば、うまくいくはず。

「まずBunを強い型システムのある言語に持っていく方が良いって主張できるんじゃない?そこに到達したら、その強い型システムを使って改善を進めるっていうのが理想的だと思う。最初のステップで完璧を求めるよりは、ずっと良いよね。」実際、彼らはそうやって進めてる。問題が出てきたら、その都度対応してるんだ。

Zigをunsafe Rustに翻訳するつもりなら、なんで翻訳ツールを作らなかったのかが理解できない。言語構造の一対一のマッピングをして、コードベースにパターンをハードコーディングできるし、友達が言ったように「正直、zig translate-cをc2rustに接続すればよかったんじゃない?」って感じ。そうすれば決定的な翻訳が得られたし、構築するのもそんなに大きな投資にはならなかっただろうし、出力は入力と同じ保証があったはず。今回のケースでは、出力は入力よりも信頼できないと思う。入力はメモリ安全ではなかったけど手書きだった。出力はメモリ安全ではない上に、雰囲気コードで、誰も目を通していない。こんな使い方でエージェントAIを乱用する意味は何なの?

Zigをunsafe Rustに翻訳するつもりなら、なんで翻訳ツールを作らなかったのかが理解できない。AIは愚かで悪意のある人を不均衡に力づけるよね。

彼らは翻訳ツールを作るために数十億ドルを集めようとしてるわけじゃないからね。

「正直、zig translate-cをc2rustに繋げればよかったんじゃない?」 c2rustから出てくるものを見たことある?ひどいよ。unsafe Cポインタのセマンティクスをunsafe Rustでエミュレートする関数のライブラリに依存してる。数年前、OpenJPEG(JPEG 2000デコーダ)でバグに悩まされてた時、誰かがそれをc2rustで動かそうとしたんだけど、変換されたunsafe RustもCコードと同じところでセグメンテーションフォルトを起こした。互換性はあるけど、安全じゃない。主なポイントは、Cやunsafe Rustで文字列操作をしないこと。これは全然向いてないツールだよ。

「正直、zig translate-cをc2rustに繋げればよかったんじゃない?」 これ、思ってるようには動かないよ。これらはエラーだらけで、コードがすごく冗長になって、理解しづらくなる。小さなアプリには使えるけど、全体のリライトには向いてない。

なぜ翻訳ツールを作らないの? 彼らは作ったよ ;) すごくダイナミックなやつを…

「もしZigをunsafe Rustに翻訳するつもりなら、翻訳ツールを作ればよかったのに。経験や判断力、何よりも謙虚さが必要だけどね。自信過剰なバイブコーダーたちは、非決定的なモデルでバベルの塔を作ろうとして、プロジェクトが失敗するのに驚いてる。」

コードベースを別の言語にポートする正しい方法は、構文木を解析して、決定的で検証された変換を適用することだったはずだよ。

正直、彼らが1週間で完全に動作させられたのにはちょっと驚いたよ。私のサイドプロジェクトも似たような目標(https://tsz.dev)だけど、成功を主張するつもりは全くない。ちゃんと動くか確認するために、どんどんテストを追加してる。TypeScriptのテストが全部通っても、予想通りのバグが見つかるしね。tscの挙動に合わせるのは本当に_本当に_ハードルが高いよ。見てみて: https://github.com/type-challenges/type-challenges LLMを使ってたくさんのコードを書くことには反対じゃないけど、今のペースでコードを出力できるようになったから、検証は100倍もっと堅牢であるべきだと思う。

そのプロセスが1週間で終わったって証拠はあるの?

「これは実験だ」と言ってから、1週間で約100万行の(おそらく)レビューされてないコードをマージするなんて、驚きだよ。エージェントを使うことには反対じゃないけど、こんな急いで進めてコミュニティを置き去りにするのは、めちゃくちゃアマチュアっぽい。新卒エンジニアがやりそうなことだね。

彼らはこれを何ヶ月も前から計画して実験してたんじゃないかな。大規模なテストスイートに加えて、エージェントを並列化するためのツールもたくさんあるし、無限のトークン予算もある。だから、あまり気にしないで。

この問題は誤解を招くね。問題はmiriがキャッチする未定義の動作の存在じゃなくて、安全なコードから未定義の動作を許すAPIを公開することなんだ。miriはそれを証明するテストを書かないとキャッチしない。これはunsafeな言語からコードを初めてポートする際に起こるのは、全然不合理なことじゃないよ。後から、bunチームがunsafeコードを正しくラップする関数を確認することはできるし、実際そうしてるみたい。ポーティング段階で一時的にいくつかのunsafe関数を安全とマークするのは、実際には大した問題じゃない。今の状態でメインリポにマージするのはちょっと変だけど、チームがこれを確実にやると決めたなら、全然不合理なことじゃない。唯一の本当の問題は、この状態で実際にリリースを作った場合だね。それに、LLMが良いテストにすごく反応するから、すぐにmiriでテストを実行するようにセットアップしなかったのはちょっと残念だな。これはこのgithubの問題のせいじゃなくて、別のテスト[1]が確実に未定義の動作を引き起こすからなんだけど、そのテストしてるコードはどこにも使われてないみたいだから、あんまり実際の問題じゃない。とはいえ、ポーティングプロセスはまだ始まったばかりだから… もしかしたらそのうちやるか、実際には必要ないunsafeコードを全部取り除くかもね。[1] https://github.com/oven-sh/bun/blob/4d443e54022ceeadc79adf54... - 最初のミュータブル参照から派生したポインタは、同じオブジェクトへの新しいミュータブル参照を作成することで無効化される。Cの用語で言うと、「ミュータブル参照」は「トリビアルな変更を通して作られる制限参照」と考えて。これを正しくやるのは簡単で、同じミュータブル参照からすべてのポインタを派生させるべきだったんだけど、正しくできてなかった。追記。GitHubをスパムするのは、オープンで作業する人が減るだけだから、やめてほしい。みんな、第三者のサイトでこの作業をしっかり判断できるから。追記の追記。公開された状態になるまで判断を保留した方がいいかも。中間の作業状態を判断するのは、あんまり公平でも面白くもないと思う。

みんなは、unsafe言語から別のunsafe言語にバインディングされたコードベースが、すぐに完璧に実装されると思ってるの?

あのスレッドのコメント、Facebookレベルの痛さだね。GitHub、ほんとにひどいことになったな!

その UB は Zig バージョンにも存在するの?ポートの時に追加されたのかな?