EmacsのTRAMPで多段SSH越しにファイルを開いた話

きっかけ 作業機Aで書いたEmacsの設定はそのままに、B経由でCにあるファイルを直接開きたくなった。CにEmacsを立てるほどでもないし、B自体も経由するだけの踏み台。よくある構成だと思う。 「TRAMPで多段SSHすれば一発で開けるはず」という認識だけはあったので、記憶を頼りに適当な構文を書いたら普通に動かなかった。 最初に書いた(間違った)構文 C-x C-f /ssh:userB@B:/ssh:userC@C:/path/to/file RET コロン区切りでリモートパスを入れ子にすれば多段になるだろう、という思い込みで書いたが、これは単に存在しないパスとして扱われて開けない。 正しい構文: パイプ区切り 調べ直すと、現行のTRAMP(Ad-hoc multi-hops)では各ホップを | で連結する仕様になっていた。 各プロキシはファイル名部分を除いたリモートホスト指定と同じ構文で指定し、ホップごとに | で区切って、起点ホストから最終目的地までを連結する、という説明になっている1。 C-x C-f /ssh:userB@B|ssh:userC@C:/path/to/file RET パスの : は最後のホップにだけ付く。それ以前のホップは | で単純に繋げるだけでよかった。 dired でディレクトリブラウズしたい場合も同じ構文でいける。 C-x d /ssh:userB@B|ssh:userC@C:/path/to/dir RET 一度使うと短縮形が使えるようになる TRAMPはこのアドホックな多段定義を、そのEmacsセッション中は tramp-default-proxies-alist に一時的なレコードとして追加する。そのため同じセッション内であれば、以降は /ssh:you@remotehost:/path という単純な形式だけで同じリモートホストに再接続できるようになる1。 セッションをまたいで使い回したい場合は tramp-save-ad-hoc-proxies を非nilにしておけば、設定ファイル側に多段定義そのものが保存される1。 ついでにsudoも同じノリで組める C上でrootが必要な作業をしたい場合、sudo を最後のホップとして追加するだけでよい。 C-x C-f /ssh:userB@B|ssh:userC@C|sudo:root@C:/path/to/file RET su、sudo、doas、run0 のようなメソッドを別ホスト上で実行したい場合、先頭にsshなどのメソッドを組み合わせて使う。つまりTRAMPはまず非管理者権限でそのホストに接続し、そのあとでそのホスト上で管理者権限に切り替える、という2段階の動作になっている1。 現在開いているバッファをそのままsudo権限で開き直したいだけなら、tramp-revert-buffer-with-sudo というコマンドが用意されている2。ファイルを開き直すたびにパスを書き直さなくていいので、これが一番出番が多いかもしれない。 M-x tramp-revert-buffer-with-sudo RET まとめ TRAMPの多段SSHは、以前は tramp-default-proxies-alist を事前に設定しておく必要があったが、Emacs 24以降は設定なしでその場でパイプ区切りの多段パスを書くだけで通るようになっている3。この「昔の記憶のまま新しい仕様を疑わなかった」せいで無駄にハマったので、次から多段SSH系のパスを書くときは一旦マニュアルを見に行くことにする。 Ad-hoc multi-hops (TRAMP User Manual) ↩︎ ↩︎ ↩︎ ↩︎ TRAMP 2.8.1 User Manual ↩︎ TRAMP - WikEmacs ↩︎

July 13, 2026 · 1 min

iptablesルールを可視化するツールを作った話

きっかけ 暇つぶしとインフラの勉強を兼ねて、何か作りたいと思っていた。最初に思いついたのは「iptablesのルールを抽出して、何が許可されてて何が拒否されてるのかをUI的に見せるアプリ」だった。 思いつきはよかったが、実際に手を動かしてみると想像より何段階も難しかった(動いたのはほぼClaude)。この記事はその過程の記録。 言語化しづらい分野だったが、ふんわりとした説明でも個人的にはかなり納得のいくものを生成してくれたので満足ではある。 作ったもの iptables-rel GitHub - wasuken/iptables-rel · GitHub 既存ツールは無かったのか 作り始める前に軽く調べた。同じ発想のツールは既にいくつかある。 gressgraph は iptables -L -vx の出力をグラフ化するツールで、開発者自身が小規模なルールセットでしか検証していないと明言している。iptable_vis はAWKスクリプトでiptablesの出力を読み、blockdiagでフローチャートを生成する。星が700超えていて、それなりに使われているようだった。 ただ、これらは基本的に「チェーンのジャンプ構造」をそのままグラフにする方向性で、IPアドレスやインターフェースの意味を解釈して「ホストAからホストBへの通信」という粒度に変換するような作り方はしていなかった。個人の環境に合わせて意味づけをするなら、これは自分で作った方が早いと判断した。 最初の設計: グラフ、失敗 D3.jsのforce-directedグラフで、ノードをホスト、エッジをルールとして表示するプロトタイプを最初に作った。動くには動いたが、実際に見てみると「関連はわかるけど、間のルールがわからない」という致命的な問題があった。エッジの上に小さくラベルを乗せるだけでは、プロトコルやポート、チェーンの情報を表現しきれない。 次に表形式(ホストごとにルールを一覧表示)を試したが、今度は逆に「どこと繋がってるか」の直感性が失われた。 最終的に落ち着いたのは、グラフとテーブルのハイブリッドだった。ノード間の関係は力学モデルのグラフで表示し、エッジをクリックすると、その2ノード間の全ルールが表形式でサイドバーに展開される。これで「全体像」と「詳細」を両立できた。 インターフェースでつなげる 実データを流し込んでみて気づいたのが、FORWARDチェーンのルールをホスト同士のエッジとしてマッピングすると意味が壊れるケースがあることだった。-i eth0 -o wg0 のようなルールは、2つのホスト間の通信を表しているのではなく、1台のマシンの中で「あるインターフェースから別のインターフェースへ中継してよいか」を表している。 ここでモデルを作り直した。ホストをそのまま1つのノードにするのではなく、ホスト:eth0 ホスト:wg0 のようにインターフェース単位でノードを分割し、ルールでない「そもそも物理的/論理的に繋がっている」という関係は、クリックできない点線のtopologyエッジとして別扱いにした。実線はルール、点線は構造。この区別を入れてから、ようやく「何が起きているか」が読み取れるグラフになった。 パーサ: 再帰的チェーン解決 iptables-save の出力は、INPUT や FORWARD のようなビルトインチェーンから、ユーザー定義のチェーンへ -j でジャンプしていく構造になっている。UFWの設定などはこのジャンプが何段にもネストしていて、素直に1行ずつ読むだけでは意味のあるグラフにならない。 実装したパーサは、ビルトインチェーンを起点に、ACCEPT / DROP / REJECT / DNAT / MASQUERADE のような終端アクションに到達するまで再帰的にチェーンを辿る。テストケースとして、意図的に未定義のチェーンへジャンプするルールや、循環参照(AがBを呼び、BがAを呼ぶ)を仕込んで、無限ループせずに unresolved として記録されることを確認した。完全なNetfilterのセマンティクスを再現しているわけではなく、あくまでヒューリスティックな近似であることは、公開ページのフッターにも明記している。 エリア表示: 凸包から矩形へ 同じサブネットに属するノードを視覚的にグルーピングしたくなった。最初は d3.polygonHull() を使って、ノード群を囲む凸包を薄い背景として描画する方式を試した。技術的には動いたが、見た目が思っていたより有機的すぎて、期待していた「かっちりした枠」の印象にならなかった。 矩形(bounding box)に切り替えた。同じグループのノード座標からmin/maxを取り、パディングを足して角丸の<rect>を描画するだけなので、実装としては凸包より単純になった。CIDRが入れ子になっているケース(/24の中に/28があるなど)は、prefix長が短いほど背面・大きいパディング・薄い色、長いほど前面・小さいパディング・濃い色にすることで、二重の枠として表現した。 インターフェースのノードについては、エリアの境界をノードの座標だけで決めると、繋がっているエッジが箱の外からいきなり生えているように見えて不自然だった。接続エッジの中間点までを座標の計算対象に含めることで、エッジが箱の内側から伸びているように見せている。 公開、しかし・・・ Cloudflare Pagesにデプロイした。ビルドコマンドとアウトプットディレクトリを指定するだけで、静的サイトとして問題なく配信できた。バックエンドは無く、ルールのパースは全部ブラウザ内で完結する構成にした。 一個ドメイン持ってたので、サブドメイン割り当てて公開し、最初デプロイしたとき問題なく表示された。 ところが微修正を加えてPushし、自動デプロイが走った直後、突然サイトにアクセスできなくなった。Cloudflare Pagesが自動で割り当てる *.pages.dev のドメインからは問題なくアクセスできる一方、独自サブドメインだけが ERR_NAME_NOT_RESOLVED で弾かれる状態だった。 ...

July 12, 2026 · 1 min

vtermのC-kバインドを追いかけていたらEatに移行することになった話

vterm -> eatした理由 vtermでC-hとかは動いたけどC-kがうまく動かなかった。 色々ガチャガチャやってたけどだるくなったときにeatというものを知ったので試してみたところ、 ターミナルとして要求していたことをほぼ達成できたため、移行することにした。 EmacsのターミナルエミュレーターをvtermからEatに移行しました | Ki_chi@Blog 今回のEat移行の設定はこの記事を参考にした。本当に感謝。 また、これまで利用していたvtermについても感謝を伝えたい。恐らく要求されることはできたとは思うが、私の熱量が足りないためにそれを実現できなかったと見ている。 発端: vtermのC-kが動かない vtermで以下のような設定を書いていた。 (defun my/vterm-send-C-k () "Send C-k to vterm terminal." (interactive) (let ((inhibit-read-only t)) (vterm-send-key "k" nil nil t))) (use-package vterm :bind (:map vterm-mode-map ("C-h" . vterm--self-insert) ("C-k" . my/vterm-send-C-k)) :config (setq vterm-term-environment-variable "xterm-256color")) C-hは狙い通り動くのにC-kだけBuffer is read-onlyエラーで弾かれる。 最初は「vterm-keymap-exceptionsのデフォルトにC-kが含まれているせいだろう」と当たりをつけたが、実際にvtermのソースを確認したところこれは誤りだった。デフォルトの除外リストは以下で、C-kは含まれていない。 '("C-c" "C-x" "C-u" "C-g" "C-h" "C-l" "M-x" "M-o" "C-y" "M-y") さらにvterm-send-key自体の実装を見ると、関数内部でinhibit-read-onlyを自前でletしている。つまりmy/vterm-send-C-kが本当に呼ばれているなら、read-onlyエラーはそもそも起きようがない。 ということは、C-kは自分が定義した関数にディスパッチされておらず、別の何か(kill-lineのような通常のEmacs編集コマンドなど)が割り込んでいることになる。C-h k C-kで実際の解決結果を確認すれば犯人は分かるはずだが、ここまで調べた時点で「そもそもvtermをやめてEatに乗り換える」という選択肢が視野に入ってきたので、根本原因の特定は一旦保留にした。 なぜEatなのか そもそもvtermのキーマップあれこれで詰まってしまったのがすべてではあるが、後付の理由もあって、 Eatは実装がすべてEmacs Lispで完結している点は素晴らしいと思う。コンパイル済みモジュールに依存しないので初期でcmakeを要求されたりしない。所詮cmakeかつlibvtermのため、ほとんどの環境で動作するとは思うが、個人的にはElispオンリーというのは少し惹かれた。 逆に怖いところ GitHub - akermu/emacs-libvterm: Emacs libvterm integration · GitHub akib/emacs-eat: Emulate A Terminal, in a region, in a buffer and in Eshell - Codeberg.org ...

July 4, 2026 · 2 min

OFFSETページネーションをそのまま使ってませんか?――PostgreSQLで速度を実測してみた

記事中で出てくる計測等は以下のリポジトリで実施した。 GitHub - wasuken/pg-offset-bench · GitHub はじめに lobste.rsで “All you need is PostgreSQL” という記事を読んだ。 「Redisやイベントストア、マイクロサービスに安直に逃げる前に、PostgreSQL単体で解決できないか考えろ」という主張を、金融システムをサンプルに証明する内容だった。 詳細については元記事を読んでくれ。 その中でキーセットページネーションに触れていて、私もOFFSETを使っていたので本当にまずいのか気になり、実際に環境を作って計測してみた。 ただし先に結論を言っておく。OFFSETが遅いのは事実だが、特定のページ番号に直接ジャンプする処理が必須な場合はOFFSETを使うしかない。キーセットは「前のページの続き」しか取れないため、任意のページへのランダムアクセスには対応できない。 逆に言えば、次へ,前へや無限スクロールで十分なケースであれば、間違いなくキーセットページネーションを使うべきだ。本記事はその判断材料として読んでほしい。 環境 PostgreSQL 17(Docker) WSL2 / ArchLinux # compose.yml services: postgres: image: postgres:17 container_name: pg-offset-bench environment: POSTGRES_USER: bench POSTGRES_PASSWORD: bench POSTGRES_DB: bench ports: - "5432:5432" volumes: - pgdata:/var/lib/postgresql/data - ./init:/docker-entrypoint-initdb.d command: > postgres -c shared_buffers=256MB -c work_mem=16MB volumes: pgdata: テーブルは100万件のarticlesテーブルを用意した。中身はどうでもいいので適当。 CREATE TABLE articles ( id BIGSERIAL PRIMARY KEY, title TEXT NOT NULL, body TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); INSERT INTO articles (title, body, created_at) SELECT 'Article ' || i, repeat('body text ', 10), now() - (random() * interval '365 days') FROM generate_series(1, 1000000) AS i; CREATE INDEX idx_articles_created_at ON articles (created_at DESC, id DESC); ANALYZE articles; OFFSETページネーションとは もっとも一般的なページネーション実装。 ...

June 27, 2026 · 3 min

メールアドレス検証のためにスパムを送るAI検出サービスの話

面白い記事を読んだ。 Don’t verify email addresses by sending spam to them — milek7.pl 要約すると、AI検出サービス「Pangram」のサインアップフォームにメールアドレスを入力すると、謎の送信元からスパムメールが届くというものだ。著者がPostfixのログを確認したところ、複数のドメイン・IPをローテーションしながら配信を試み、DNSBLに弾かれたら別のIPで即リトライするという、本格的なスパム業者のインフラが動いていたとのこと。 「届いた=有効なアドレス」として検証完了とみなす設計らしく、著者は「スパムを送ることがメール検証になっている」と皮肉っている。詳しくは元記事を読んでほしい。技術的な詳細(Postfixログや送信ドメイン一覧)もすべてそちらに載っている。 気になったので Pangram 自体を調べてみた。 Pangram とは 公式サイトによると、AI生成コンテンツを検出するサービスだ。ChatGPT・Claude・Geminiなどを99.98%の精度で検出するとうたっており、大学・企業向けに展開している。 About ページによれば、創業者のMaxとBradleyはStanfordの同期で、それぞれNuro・Google・Teslaなどでキャリアを積んだMLエンジニア。学生スタートアップではなく、業界経験者が立ち上げたサービスだ。シカゴ大学・メリーランド大学の研究者による第三者検証も受けており、SOC2 Type2認証も取得している。 それなりに本格的なサービスなので、元記事で指摘されているメール検証の実装が余計に不可解になる。 なお日本語ページも用意されているが、翻訳の品質はかなり怪しい。「のグローバルブランドから信頼されています」「AI生成コンテンツ( )および盗用を検出する」など、テンプレート変数の埋め忘れや機械翻訳そのままと思われる箇所が散見される。他の言語は見てないし、調べることもしてないのでまあ日本語が難しいとはいえばそうかもしれない。 日本のXでの使われ方 日本のXでAI判定ツールとしてそこそこ広まっていることがわかった。 使われ方は「この投稿AI臭い → Pangramで判定 → AI!」という流れがしばし見られる。 また、この記事はAIかどうかについては知りたいという意見も散見された。 誤検知は確率的に必ず起きる Princetonの研究者Arvind Narayananは、誤検知率1万件に1件という数字を前提にしても、大学4年間で数百〜1000件の提出物をチェックすれば、学生の一定割合が誤って不正行為を疑われる計算になると指摘している。 SNSの投稿を個人特定の「証拠」として使えば、誤検知による風評被害は避けられない。 生成AIを利用してる私がいうのもなんだが、事実として知っておきたい人たちは存在しつつも 現状のインターネットを見る限り、いわゆる魔女狩りに利用されそうだ。 とはいえ、生成AIを利用していないことを明記しないほうが悪いので 私もこれからは記事に明記しようと思う。 ツール自身も断定を戒めている Pangram自身も「AI検出はひとつのシグナルであり、追加の証拠収集と組み合わせて使うべき」と公式に述べている。個人のX投稿に対して「これはAIが書いた」と断定する用途はそもそも想定されていない。 サービスの信頼性自体に疑問符がついた 元記事の指摘が事実であれば、メールアドレス検証のためにスパム業者へアドレスを渡している可能性がある。現時点でPangram側からの公式コメントは確認できておらず、あくまで一個人の調査に基づく疑惑の段階だ。ただ、AI判定ツールとして人を断罪する根拠に使うには、信頼性の検証が先だろうという気持ちにはなる。 元記事の著者は「どうやってこんな実装に辿り着いたのか謎。LLMエージェントが暴走した可能性もある」と書いていた。AI検出ツールが、AIの暴走疑惑を持つインフラを抱えているとしたら、皮肉としては出来すぎている。 参考 Don’t verify email addresses by sending spam to them — milek7.pl Pangram 公式サイト(日本語) Pangram About Us Arvind Narayanan — 誤検知率についての指摘

June 24, 2026 · 1 min

「flag」って名前つけるのをやめろという記事を読んだ

RSSで Stop Naming Your Variables “Flag”: The Art of Boolean Prefixes という記事が流れてきた。bool変数の命名規則の話。 記事の要点 主張はシンプルで、bool変数にflagやdoneのような中身のない名前をつけるな、というもの。flagやdoneは「何の」フラグなのか、「何が」完了したのかが変数名から一切わからず、読み手は呼び出し元や定義側のコードを読みに行かないと意味が取れなくなる。 これを解消するために、is(状態)、has(包含)、can(機能)、should(意図・ビジネスルール)の4つのプレフィックスで命名すれば大半のケースをカバーできるとしている。あわせて「ネガティブな名前を使うな」というルールも強調されていて、isDisabledのような名前は将来!isDisabledという二重否定を生むので避け、isEnabledのように常にポジティブな形で持つべきだとしている。 もう一つ面白かったのが、これらのルールは状態を表すプロパティにはよく効くが、引数として渡されるboolには効かないという指摘。意味の伴わないbool引数を複数並べたメソッドは、呼び出し側のコードを読んだだけでは何が起きるか判断できない設計上の欠陥で、これを解消する手段としてメソッド分割・Enum・Configオブジェクトの3パターンが挙げられている。 詳細は元記事を読んでほしい。 「メソッド分割って結局引数じゃないの?」を考えた ここから自分の考察。メソッド分割・Enum・Configオブジェクトの3パターンが並列に挙げられているのを見て、「メソッド分割だけ毛色が違うのでは」と思った。EnumもConfigオブジェクトも結局は引数として何かを渡している。メソッド分割だけが「引数を使わない」解決策に見える。 整理すると、これは「引数を使うかどうか」の話ではなく「型に意味を持たせているかどうか」の話になる。bool単体は型として「真か偽か」以上の情報を持たない。これを呼び出し側で見た時に何を意味するか判断できないのが元の問題で、3パターンはどれも「呼び出し側だけで意味が完結する形に情報を昇格させる」という同じ操作をしている。メソッド名に意味を込めるか、Enumの値に意味を込めるか、オブジェクトのプロパティ名に意味を込めるかの違いでしかない。 この観点で見ると使い分けの基準も見えてくる。状態が二択でこの先も増えない見込みならメソッド分割で呼び出し側の読みやすさを最大化できる。一方、状態が3つ以上ある、または今後増える見込みがあるならメソッド分割は破綻しやすい。「即時送信」と「キュー送信」の2メソッドに「リトライあり」という軸が加わると、組み合わせの数だけメソッドが必要になり、命名が事故る。こうなったらEnumかConfigオブジェクトに切り替えるべきサイン。 経験則として、メソッド名に「And」を入れたくなった瞬間が分割すべきタイミングだと思っている。「送信かつキュー投入」のような名前は、本来排他な選択肢を無理やり1つの操作として表現しようとしているか、1つの関数が複数の責務を抱え込んでいるかのどちらかで、どちらにせよ設計を見直すべきサインになる。 長い変数名・関数名はなぜダメなのか ここからは命名の話を少し広げる。booleanの話とは別に、変数名や関数名が長すぎることそのものを問題視する意見もよく見る。これはbooleanの「曖昧すぎる」問題とは逆方向の弊害で、原因は大きく2つに分けられると思う。 一つは、命名で背負わせている責務が多すぎること。getUserDataAndValidateAndSendNotificationのような名前は、まさに「And」がそのまま並んでいる例で、これは命名の問題ではなく関数の設計自体の問題。複数の責務を1つの関数に詰め込んだ結果として名前が膨張している。この場合、長い名前を短くしようとするのではなく、関数自体を分割して短い名前に戻すのが正しい対処になる。 もう一つは、文脈の不足を変数名で無理やり補おうとしていること。例えばuserServiceUserIdForBillingCalculationのような名前は、本来クラス名や引数の型、コメント、あるいはスコープの狭さで伝えられるはずの文脈を、変数名一つに全部押し込めようとして起きる。クラス名がすでにBillingCalculatorなら、そのメソッド内でuserIdとだけ書けば文脈はクラス名から自然に補完される。長い名前が必要になっている時点で、実はそのクラスやモジュールの設計自体が文脈を提供できていない、というシグナルとして読むこともできる。 つまり長すぎる名前は、それ単体の修正対象というより「設計のどこかに無理が生じている」ことを教えてくれる兆候として扱うのが妥当だと思う。 デザインパターンとの接続 Boolean Trapの3パターン(メソッド分割・Enum・Configオブジェクト)は、実はそれぞれ古典的なデザインパターンの入り口になっている。 メソッド分割を突き詰めると、状態ごとに振る舞いを切り替えるクラス設計が必要になる場面が出てくる。状態の数がさらに増え、状態遷移そのものに意味を持たせたくなったら、Stateパターン(状態をオブジェクトとして切り出し、状態ごとに振る舞いをカプセル化する)に近づいていく。 Configオブジェクトは、複雑な初期化や生成のロジックを切り出したくなった時点でBuilderパターン(オブジェクトの組み立て手順そのものを別オブジェクトに任せる)と接続する。new ExportOptions { ... }のようなオブジェクトリテラルで足りているうちはConfigオブジェクトのままでいいが、必須項目とオプション項目が増えて初期化の組み合わせ自体が複雑になったら、Builderへ移行するタイミングになる。 Enumで振る舞いを切り替える設計が増えてくると、Strategyパターン(アルゴリズムや処理そのものを差し替え可能なオブジェクトとして扱う)とも近づく。Enumの値でswitch文を量産し始めたら、それは「Enumに対応する振る舞いをオブジェクトとして注入する」設計に切り替えるサインになる。 いずれも、最初からこれらのパターンを持ち出す必要はなく、Boolean Trapの3パターンで対処しているうちに複雑さが増した時に「次に進む先」として用意されている、という位置づけで捉えるのがちょうどいいと思う。 クリーンアーキテクチャとの接続 もう一段引いて見ると、これらの命名・設計判断は層の責務分離とも関係してくる。 クリーンアーキテクチャでは、ドメイン層(ビジネスルール)とインフラ層(外部APIやDBとのやり取り)を明確に分離する。「ネガティブな名前は外部APIとの境界線だけに押し込めて、ドメイン層には持ち込まない」という元記事のルールは、まさにこの境界の話そのものになっている。外部の都合(ネガティブな命名、bool引数の羅列)はインフラ層やアダプタ層で吸収し、ドメイン層には意味の伴った型(Enum、Stateオブジェクト、Configオブジェクト)だけを渡す、という設計にすれば、層をまたぐたびに命名の濁りが伝播するのを防げる。 逆に言うと、ドメイン層の奥深くまでbool引数の羅列やネガティブな命名が浸透している場合、それは単なる命名の問題ではなく、本来インフラ層で吸収すべき外部の都合がドメイン層まで漏れ出している設計上の兆候として読むこともできる。 まとめ booleanの命名規則自体はそこまで難しい話ではないが、そこから「なぜそのルールが効くのか」を自分で詰めていくと、長い変数名の話、デザインパターン、層の分離まで地続きにつながっている。命名は単体の作法というより、設計全体の健全さを映す表面の一つだと捉えておくとよさそうだった。 元記事

June 21, 2026 · 1 min

RSSで流れてきたlittlefsを読んだのとFatFsとの比較

RSSを流し見してたら littlefs の DESIGN.md が流れてきた。マイコン向けのファイルシステムで、停電耐性を本気で解こうとした設計が面白かったので読んだ。 詳細な設計の話は DESIGN.md に全部書いてあるのでそっちを読んでほしい。ここでは要点だけ。 littlefs とは マイコン向けの組み込みファイルシステム。ターゲットは RAM 約 32KiB、ROM 約 512KiB 程度の 32bit マイコン + SPI NOR フラッシュ。 設計上の制約が 3 つある。 停電耐性 — 書き込み中のどのタイミングで電源が落ちても壊れないこと ウェアレベリング — フラッシュの特定ブロックに書き込みが集中して早死しないこと RAM 上限保証 — ファイルシステムのサイズが増えても RAM 使用量が増えないこと マイコンは「シャットダウン処理」という概念がないので、停電耐性は必須要件になる。 既存 FS の問題点 DESIGN.md では既存のアプローチを 4 つ整理している。 方式 例 停電耐性 ウェアレベリング ブロックベース FAT, ext2 ✗ ✗ ログ型 JFFS, SPIFFS ✓ ✓ ジャーナリング ext4, NTFS ✓ ✗ COW btrfs, ZFS ✓ △(ルートに集中) ログ型は GC が O(n²) か O(n) RAM のどちらかになる。COW は更新がルートまで伝播して特定ブロックにウェアが集中する。どれも一長一短。 ...

June 19, 2026 · 2 min

FFmpegの21連ゼロデイ事件とメモリ安全言語の話

元記事 21 Zero-Days in FFmpeg — For $1,000 セキュリティスタートアップのDepthfirst社が開発したAIエージェントが、FFmpegから21件の未公開脆弱性(ゼロデイ)を発見した。かかった費用はわずか1,000ドル。 人間が数ヶ月かけて監査する規模のコード(約150万行のC言語)を数日でスキャンし、実際にクラッシュやコード実行を起こせる実証コード(PoC)までAIが自動生成したらしい。 その中でもAV1 RTPデパケタイザのバグが、ハッカーにとって都合が良すぎる完璧な仕様だった。 特別なフラグも不要で、以下の ordinary なコマンドで罠URLを開かせるだけでリモートコード実行(RCE)が成立する。 ffmpeg -i rtsp://attacker/stream わずか183バイトのパケット1つでPCが乗っ取られる仕組みが面白かったのでメモ。 原因 トラック用カーソルの「勘違い」 問題は libavformat/rtpdec_av1.c のAV1 RTPデパケタイザ(パケット組み立て処理)にある。 AV1の仕様では、フレームの区切りを示す1バイトのマーカー Temporal Delimiter (TD) を「無視して削除(ignore and remove)」せよ、と書かれている。この処理部分のロジックがバグっていた。 FFmpegは、パケットの書き込み位置を制御するカーソル pktpos を動かしながら処理を行うが、TDを見つけたときに以下のような挙動をしていた。 // libavformat/rtpdec_av1.c:250 if ((obu_type == AV1_OBU_TEMPORAL_DELIMITER) || (obu_type == AV1_OBU_TILE_LIST)) { pktpos += obu_size; // 書き込みカーソル(矢印)だけを先に進める rem_pkt_size -= obu_size; // インプットカウンターを減らす obu_cnt++; continue; // メモリ確保(割り当て)は行わない! } データを無視する(メモリを確保しない)のに、書き込みカーソル(pktpos)だけを先に進めてしまった。これにより、確保されたサイズより遥か先を指す「ポイズンカーソル」が生まれる。 さらに、インプット側のポインタを進め忘れたため、次のループ処理で同じTDのデータを再パースし、攻撃者が用意した任意のデータを、はみ出た未来のメモリ番地へピンポイントに書き込める状態(ヒープバッファオーバーフロー)が完成する。 詳しくてちゃんとした解説については21 Zero-Days in FFmpeg — For $1,000の最後らへんを見てほしい。 綺麗すぎるメモリ配置 通常ならクラッシュして終わるが、FFmpegのアロケータの並びがハッカーにとってお宝だった。 動画データを置くバッファのすぐ隣(オフセット152)には、内部の管理用構造体 AVBuffer が隣接して割り当てられる決まりになっている。この中には、データの片付け(メモリ解放)を行うための「関数ポインタ」(void (*free)(...))が格納されている。 ...

June 13, 2026 · 1 min

AURの大規模マルウェア事件、自分の環境は大丈夫だった話

2026年6月、Arch Linux の AUR (Arch User Repository) で大規模なマルウェア混入事件が発生したらしい。 Arch Linux ユーザーとして、自分の環境への影響を確認したメモ。 何が起きたか Phoronixの報道によれば、400以上のAURパッケージが今週の大規模マルウェアキャンペーンで侵害されたとのこと 参考: https://www.phoronix.com/news/Arch-Linux-AUR-400-Compromised 事件は Arch Linux の公式メーリングリスト aur-general で報告・追跡されている 公式スレッド: https://lists.archlinux.org/archives/list/[email protected]/thread/FGXPCB3ZVCJIV7FX323SBAX2JHYB7ZS4/ 攻撃の手口 セキュリティベンダーSonatypeの分析(通称 “Atomic Arch”)によると、攻撃者はメンテナーが放棄した(orphaned)AURパッケージの所有権を奪い、ビルド手順(PKGBUILD)を改変して atomic-lockfile という悪意あるnpmパッケージをインストールさせる、というパターンが確認された。 参考: https://www.sonatype.com/blog/atomic-arch-npm-campaign-adds-malicious-dependency この atomic-lockfile は preinstall フックで Rust製のELFバイナリ(deps)を自動実行し、ブラウザやDiscord、GitHub、SSH、Dockerなど開発環境の認証情報を狙う情報窃取マルウェア 参考: https://ioctl.fail/preliminary-analysis-of-aur-malware/ 具体例として、VRストリーミング用パッケージ alvr に npm 関連の不自然な処理が追加されていたことが報告されている 参考: https://linuxiac.com/arch-linux-aur-malware-campaign-hits-multiple-user-contributed-packages/ 自分の環境を確認した paru -Qm でAUR(foreign)パッケージ一覧を出し、第三者がgit logから抽出した影響パッケージ一覧(gr.ht/aur_pkg_list.txt)と照合しました。手元の環境(google-chrome, postman-bin, ngrok など17パッケージ)はリストに一件も含まれてなかった ただしこのリストは非公式・暫定的なもので、対象期間も「直近48時間」程度のため、今後も対象が増える可能性がある。 今後の運用方針 緊急対応は不要と判断したが、完全に無視するのではい。 次回 paru -Syu 実行時に、各パッケージのPKGBUILD差分(特にbuild/installセクション)をざっと確認する binパッケージ(google-chrome, postman-binなど)はメンテナー変更の有無を paru -Si <pkg> で時々確認する というくらいのライトな運用はしようかなと思った。 今回のパターンは「放棄パッケージの乗っ取り」が中心なので、現役メンテナーがついているパッケージなら当面のリスクはかなり低いだろうがね。 所詮メモなので、注意喚起とかやりましょうとか強く推奨はしないがやったほうがいいとは思うかな。

June 12, 2026 · 1 min

svg-line: EmacsのステータスバーをSVGで統一する試み

元記事 svg-line: Better Status Bars for Emacs Emacsのmode-line、header-line、tab-bar、tab-lineはそれぞれネイティブAPIレベルで挙動が違い、多段表示・右寄せ・アイコン・マウスイベントの扱いがバラバラという問題がある。svg-lineはこれらをSVG画像として描画することで挙動を統一するパッケージ。 SVGにすることで座標ベースのマウスイベント検出ができるのも地味に大きい。ネイティブAPIだとクリックやホバーが*-lineごとに対応がバラバラだったのが一気に解決される。 この記事に触発されて私も少し触ってみようと思った。 あわよくばこのまま置き換えてもいいかとも思う。 やったこと 既存パッケージの整理 svg-lineを試すために一旦以下を削除・調整した。 nyan-mode → 削除 minions → 削除 spacious-padding → :mode-line-widthの行だけ削除(他の余白設定は残す) svg-lineの導入 (use-package svg-line :straight (:host github :repo "chiply/svg-line")) mode-lineを書いてみた 最小構成から始めて、バッファ名・git branch・メジャーモード・マイナーモード・行列数の2行構成を目指した。 (svg-line-define 'my-mode-line :target 'mode-line :active #'mode-line-window-selected-p :background (lambda () (face-background 'mode-line nil t)) :foreground (lambda () (face-foreground 'mode-line nil t)) :content (lambda () (list (list :left (list (if (buffer-modified-p) "● " " ") (buffer-name)) :right (list (or (and (fboundp 'vc-git--symbolic-ref) (buffer-file-name) (vc-git--symbolic-ref (buffer-file-name))) ""))) (cons (list (symbol-name major-mode)) (list (format-mode-line "%l:%c")))))) (svg-line-activate 'my-mode-line) シンプルにはなったが、やはりnyan-modeがないとさみしい。 ...

June 9, 2026 · 1 min