並行して動く複数のAIエージェントが同じPRを触ると、PRのheadを読んでからpushするまでの間に、別のエージェントが先にpushしていることがあります。pushの直前にheadを再取得すれば、この衝突に気付けます。
この記事でわかること
- TOCTOUは、攻撃がなくても、確認と使用の間に別の処理が割り込む余地があれば起きます
- 実際の衝突は、同じ指摘を二重に直すものと、修正より先にマージされてしまうものの2種類でした
- headが進んでいたら相手の修正を読み、同じ穴が塞がっていれば自分の変更を取り下げます
TOCTOUとは
TOCTOU(Time-of-check Time-of-use)は、リソースの状態を確認してから実際に使うまでの間に状態が変わり、確認の結果が無効になる不具合です。 典型は、ファイルへの書き込み権限を確認してから開くまでの間に、シンボリックリンクでファイルを差し替える攻撃です。1
起きるかどうかは、攻撃の有無ではなく、確認と使用の間に別の処理が割り込む余地があるかどうかで決まります。
同じPRを複数のAIエージェントが触る
このシステムでは、AIエージェントをAnthropicのクラウドで定期実行し、実装とレビューを回していました。以下、エージェントの1回の実行をジョブと呼びます。実装を進めるエージェントと指摘を出すレビュアーは、別々のスケジュールで起動し、同じPRを対象に動くこともありました。PRのheadを読む「確認」と、そのheadを基点に修正を積む「使用」の間に、別のジョブが割り込む余地があったのです。
自分のジョブが読んだheadは、読んだ瞬間の値でしかありません。修正を作っている間に別のジョブが同じPRへpushすれば、headは進みます。図のとおり、確認したときのheadと、使用するときのheadが食い違います。
例えば、2人の担当者が同じ見積書の同じ誤りに気付き、それぞれ直して提出するとします。提出の直前に共有フォルダの最新版を見なければ、相手がもう直していたことに気付けないおそれがあります。
実際に起きた2つの衝突
衝突は2種類ありました。
同じ指摘を二重に直す
あるPRについた、マージを止めるほど重大な指摘(BLOCKER)に対応するため、読んだ時点のheadを基点に修正していました。その途中で、別のジョブが同じ指摘を先に直してpushしていました。pushの直前にheadを再取得したので気付けました。読んだときとpushするときのheadが違えば、その間に別の変更が入っています。
修正より先にマージされる
別の日には、CHANGES_REQUESTEDの判定が付いたPRのブランチに、修正をコミットしていました。その最中に、別のジョブが指摘前の古いコミットのままPRをマージしました。修正はできていましたが、マージ済みのブランチに積んでもmainブランチには入りません。
修正が終わったことと、その修正がmainに入ることは別の事実です。修正がmainに入るかどうかは、PRがまだopenかを機械的に確認しないと分かりません。
2つは症状が違っても、原因は同じです。修正に手間取り、確認から使用までが長引くほど、衝突は起きやすくなります。
pushの直前に再取得する手順
手順は次の7つです。
| # | 手順 | 理由・注意 |
|---|---|---|
| 1 | 着手前にheadを読む | — |
| 2 | 修正を作る | ここで時間がかかる |
| 3 | pushの直前にheadを再取得する | 作業中に起きた変化を検出する |
| 4 | headが進んでいたら、相手の修正を読む | 同じ穴を別の形で塞いだかもしれない |
| 5 | 相手の修正が同じ穴を塞ぐか、実際に確認する | 「直した」と書いてあるだけでは確認にならない |
| 6 | 塞がっていれば自分の変更を取り下げる。force pushで上書きしない | 相手の検査ごと消さない |
| 7 | pushの直前にPRがopenかを確認する | マージ済みのブランチに積まない |
表の中で大事なのは4と5です。headが進んでいたからといって、相手の修正を読まずに自分の版で上書きしてはいけません。それでは確認を1回増やしただけです。指摘された入力で再現を試し、問題が起きないことを確認して初めて、「塞がっている」と言えます。 塞ぎ方が違うだけなら、相手の実装を土台にして、足りない差分だけを積みます。
確認から使用までの時間を短くする
TOCTOUの一般的な対策は、確認と使用をアトミックな操作にすることです。1 しかし、複数のAIエージェントが同じPRを編集する場面では、それが難しくなります。
そこで、使用の直前に確認し直して、確認から使用までの時間を短く保ちます。変更を取り下げるときは、「自分の作業のほうが良いはず」という思い込みで上書きせず、取り下げた理由をPRのコメントに書きます。自社の運用でも、「確認」と「使用」に当たる操作を書き出し、使用の直前に確認し直す手順を1つ足すところから始められます。
よくある質問
Q. ロックを取れば防げませんか。
GitHubのPRという共有リソースに対して、独立に動く複数のAIエージェントが排他ロックを取り合う仕組みはありませんでした。ロックの取得と解放そのものが、新しいTOCTOUを生むこともあります。そのため、衝突を検出したら取り下げる規則を、現実的な対策として選びました。
Q. 衝突はどのくらいの頻度で起きますか。
スケジュールが重なる時間帯ほど起きやすくなります。このシステムでは、指摘対応と実装のジョブが同じ1時間に何度か起動することがあり、その1時間の中で複数件の衝突がありました。
出典
- CWE-367: Time-of-check Time-of-use (TOCTOU) Race Condition(参照 2026-08-14)

