VirtualBox の共有フォルダで、書き込みが無限ループしてディスクが埋まる不具合を見つけた話

2026-09-20 公開 / Linux カーネル 7.0 系の vboxsf ドライバーで確認。修正パッチと DKMS パッケージは GitHub に置いています。

要点だけ読みたい方は note 版へ。

3行まとめ

  • VirtualBox の共有フォルダに「まだ読み込まれていないメモリのページ」から書き込むと、カーネルの中で書き込み処理が無限ループし、中身がゼロのファイルが延々と増え続けてディスクが埋まります
  • 原因は fs/vboxsf/file.cvboxsf_write_end() が、コピーできなかった分まで「書けた」と返していること。Linux 7.0 と最新のソース(master)で同じままです。
  • 2行の修正で直ります。修正版のドライバーは DKMS の形で配布しています。

こんな症状が出たら、この記事の不具合かもしれません

  • VirtualBox のゲスト(Linux)で、共有フォルダに数バイトのファイルを作らせただけなのに、ファイルサイズが MB 単位で増え続ける。中身を見ると先頭からゼロ。
  • その書き込みを行ったプロセスが終わらない。Ctrl+C では止まらず、kill -9 でだけ止まる。
  • dmesg に次のような警告が大量に出る。
    WARNING: lib/iov_iter.c:624 at iov_iter_revert+0x1fc/0x270, CPU#3: <スレッド名>
  • /var/log/syslog、/var/log/kern.log、journal が数GB 単位で膨らみ、ディスクが埋まる。
  • ゲストで virtiofsd を動かしている場合、警告を出すのは vring_worker というスレッド。

英語で検索する人向けのキーワード:vboxsf infinite loop, vboxsf write hangs, iov_iter_revert warning vboxsf, virtiofsd on vboxsf, VirtualBox shared folder file grows zeros.

何が起きていたのか

引き金

Linux がファイルに書き込むとき、カーネルはまず、書き込み元のデータをページ単位でコピーします。このコピーは「ページフォルトを起こさない」やり方で行われるので、書き込み元のページがまだ読み込まれていないと、0 バイトで失敗します

これは異常ではありません。失敗したら、カーネルはページを読み込んでからやり直します。ext4 などでは日常的に起きて、問題なく処理されています。

普通のアプリは、直前に自分で用意したデータを書き込むので、この失敗はまず起きません。起きるのは「mmap したファイルや共有メモリを、一度も読まずにそのまま別のファイルへ書く」場合です。virtiofsd はまさにこれに当てはまります。

vboxsf の処理

fs/vboxsf/file.cvboxsf_write_end() は、カーネルから「要求した量(len)」と「実際にコピーできた量(copied)」を受け取り、ホストに書いた量を返す関数です。問題はここです。

	u32 nwritten = len;          /* ← copied ではなく len で初期化 */
	...
	/* zero the stale part of the folio if we did a short copy */
	if (!folio_test_uptodate(folio) && copied < len)
		folio_zero_range(folio, from + copied, len - copied);

	buf = kmap(&folio->page);
	err = vboxsf_write(sf_handle->root, sf_handle->handle,
			   pos, &nwritten, buf + from);   /* ← len バイト分を書く */
	...
	return nwritten;             /* ← コピーに失敗していても len を返す */
Code language: C++ (cpp)

コピーが 0 バイトで失敗しても、足りない分をゼロで埋めたうえで要求された量を全部ホストに書き、「全部書けた」と返します

呼び出し側の反応

呼び出し側(mm/filemap.cgeneric_perform_write())はこうなっています。

	copied = copy_folio_from_iter_atomic(folio, offset, bytes, i);
	status = a_ops->write_end(iocb, mapping, pos, bytes, copied, folio, fsdata);
	if (unlikely(status != copied)) {
		iov_iter_revert(i, copied - max(status, 0L));
		...
	}
	if (unlikely(status == 0)) {
		/* A short copy made ->write_end() reject the thing entirely. ... */
		if (copied) { bytes = copied; goto retry; }
		if (fault_in_iov_iter_readable(i, bytes) == bytes) { status = -EFAULT; break; }
	} else {
		pos += status;
		written += status;
	}
	} while (iov_iter_count(i));
Code language: C++ (cpp)

copied = 0status = bytes になると、次の順で破綻します。

  1. iov_iter_revert(i, 0 - bytes) … 負の値が巨大な数として渡り、WARN_ON が発火して何もしない(=あの警告)
  2. status != 0 なので、ページを読み込む fault_in_iov_iter_readable() が呼ばれない
  3. pos += status書き込み位置だけ進む
  4. データは1バイトも消費されていないので、ループの先頭に戻る。ページは読み込まれないままなので、永久に繰り返す

ファイルがゼロのまま増え続ける、警告が大量に出る、kill -9 でしか止まらない(ループの先頭に fatal_signal_pending() の確認があるため)といった症状が、すべてこれで説明できます。

コードのコメントが言っているとおり、write_endコピーが足りなければ受け付けを拒否(0 を返す)すべきでした。

データが静かに壊れる可能性

同じ原因で、暴走しない場合でも危険があります。既にページが読み込み済み(uptodate)の状態で書き換えると、ゼロ埋めの処理はスキップされます。そこでコピーが途中で失敗すると、ページに残っている古い内容をホストに書いて「全部書けた」と返します。エラーは出ません。こちらは未検証ですが、コードの上では起こり得ます。

修正

-	u32 nwritten = len;
+	u32 nwritten = copied;
 	...
 	if (!folio_test_uptodate(folio) && copied < len)
 		folio_zero_range(folio, from + copied, len - copied);

+	/* Nothing copied: reject so generic_perform_write() faults in and retries */
+	if (!copied)
+		goto out;
+
 	buf = kmap(&folio->page);
Code language: Diff (diff)

コピーできた分だけを書き、その量を返します。0 バイトのときは何も書かずに 0 を返し、呼び出し側にページを読み込んでやり直してもらいます。

修正版で確認した結果です。

確認項目修正前修正後
未読み込みのページから 6 バイト書き込み暴走(22,286 バイトまで増加、警告多数)6 バイト、内容一致
一部だけコピーできるケース未確認内容一致
既存ファイルの一部書き換え未確認先頭だけ変化、残りは元のまま
virtiofsd 経由でのテキスト・PNG の作成、コピー、追記、書き換え暴走すべて正常、カーネルの警告 0

影響範囲と、当てはまらない条件

  • 発生する場所
    VirtualBox のゲスト(Linux)で、共有フォルダ(vboxsf) に書き込むとき
  • 対象のドライバー
    Linux カーネルに含まれる vboxsf(Oracle の Guest Additions に含まれる版は別実装で、未確認)
  • 確認した版
    Ubuntu 26.04.1、カーネル 7.0.0-31-generic。ソース上は Linux 7.0 と master(2026-09 時点)で同じコード
  • 引き金になる書き方
    mmap したファイルや共有メモリを、読まずにそのまま書き込む。virtiofsd が典型
  • 影響の範囲
    ゲストの中に閉じる(ゲストのディスクが埋まる、プロセスが止まる)。ホストには波及しない
  • 普通のアプリ
    ほぼ当てはまらない(直前に用意したデータを書くため)

昔からあるコードですが、条件が珍しいため表に出ていなかったと考えています。似た種類の不具合は、iomap(2023年、短いコピーでの無限ループ)や FUSE(2015年)でも見つかって修正されています。

対処方法

1. 修正版のドライバーを入れる(推奨)

GitHub に、修正パッチと DKMS のパッケージを置いています(→ https://github.com/kentaro-shiomi/virtualbox-vboxsf-endless-write-loop-fix)。DKMS なので、カーネルを更新しても自動でビルドし直します。

git clone https://github.com/kentaro-shiomi/virtualbox-vboxsf-endless-write-loop-fix
cd virtualbox-vboxsf-endless-write-loop-fix
sudo ./scripts/install.sh
Code language: Bash (bash)

署名のないドライバーになるため、Secure Boot が有効な環境では読み込めません。 その場合は 2 か 3 で対処してください。

2. 引き金を避ける

書き込み元のページを、書く前に一度読んでおけば発生しません。自分のプログラムであれば、これで回避できます。virtiofsd のように他人のプログラムが書き込む場合は、この方法は使えません。

3. 共有フォルダを使わない

VM とホストの間のやり取りを、SMB や SFTP など別の方法に替えます。vboxsf には sendfile まわりの古い不具合(Vagrant のドキュメントでも sendfile を無効にするよう案内されています)もあるので、重要なデータを扱うなら検討の余地があります。

安全策:修正版でなければマウントしない

DKMS のビルドがカーネル更新で失敗したとき、気付かないまま元のドライバーで共有フォルダを使ってしまうと、また暴走します。読み込まれている vboxsf が修正版かを起動時に確認し、違えばマウントしないようにしておくと安心です。確認用のスクリプトと systemd のユニットも、リポジトリに入れています。

上流への報告について

2026年9月20日、vboxsf の保守担当(Hans de Goede さん)と Linux カーネルのメーリングリスト(linux-fsdevel、linux-kernel)にパッチを送り、査読待ちです。Ubuntu にも報告しました。

カーネルのメーリングリストのスレッド:GitHub の README(Upstream status)にリンクがあります。

Ubuntu(Launchpad)

なお、調査の途中で別の不具合も見つかっています。vboxsf のファイルを O_DIRECT 付きで開くと、開く処理は正しく失敗するのに、後始末で NULL ポインターを参照してカーネルが異常終了します。

BUG: kernel NULL pointer dereference, address: 0000000000000020
RIP: 0010:vboxsf_release_sf_handle+0x1b/0xd0 [vboxsf]
Call Trace:
 vboxsf_file_release+0x34/0x50 [vboxsf]
 __fput+0xed/0x2d0
 ...
 ? __x64_sys_openat+0x5f/0xa0Code language: PHP (php)

こちらは今回のパッチの対象外です。

おまけ:どうやって見つけたか

Claude Desktop(Linux 版)の Cowork を VirtualBox の VM の中で動かし、作業フォルダに VirtualBox の共有フォルダを指定したところ、「6バイトのテキストを作って」という依頼で 29KB まで膨らみ、放っておくと数百MB に達しました。Cowork の中では virtiofsd がファイルを扱うため、上で書いた条件をそのまま踏んだわけです。

切り分けの流れは、続きの記事で紹介する予定です(Cowork も virtiofsd も使わない、数行の再現コードまで落とせました。再現コードは、上流の対応が進むまで公開を控えます)。

※ この記事の内容は、筆者の環境(Windows 11 ホスト、VirtualBox 7.2.16、Ubuntu 26.04.1 ゲスト、カーネル 7.0.0-31-generic)での確認に基づきます。同じ症状に心当たりのある方、別の環境で再現した方は、Xや GitHub の Issue で教えていただけると助かります。