macOS (Apple Silicon) に ClamAV を入れて自分のホームをまるごとスキャンする
ClamAV は商用 AV と違って 自分のマシンで何が起きているか可視化しやすい のが面白い。
Apple Silicon の Mac (M1/M2/M3) で brew install から ホームディレクトリのフルスキャン完了 までを実機で走らせ、所要時間と途中で詰まった点を全部記録した。
検証環境は次のとおり。
| 項目 | 値 |
|---|---|
| OS | macOS 26.3.1 (Tahoe / Build 25D771280a) |
| CPU | Apple Silicon (arm64) |
| Homebrew | /opt/homebrew (Apple Silicon 配置) |
| ClamAV | 1.5.2 (bottle) |
| ホーム合計 | 286 GB (うち Parallels 117 GB / Library 102 GB) |
Intel Mac の人は Homebrew のパスが
/usr/localになるので、本記事中の/opt/homebrew/...を/usr/local/...に読み替えてください。
1. インストール (実測 14 秒)
brew install clamav依存込みで bottle が落ちてきた。
==> Installing clamav dependency: libmagic
🍺 /opt/homebrew/Cellar/libmagic/5.47: 367 files, 13MB
==> Installing clamav dependency: yara
🍺 /opt/homebrew/Cellar/yara/4.5.5: 49 files, 2.4MB
==> Installing clamav
🍺 /opt/homebrew/Cellar/clamav/1.5.2: 181 files, 57.8MB
brew install clamav 8.52s user 4.40s system 90% cpu 14.227 total合計 73 MB。libmagic と yara が一緒に入るのは ClamAV のシグネチャに YARA ルールが使われるから。
brew info の Caveats が大事で、設定ファイルは .sample 拡張子で置かれている だけ。コピーして編集しないと freshclam も clamscan も動かない。
To finish installation & run clamav you will need to edit
the example conf files at /opt/homebrew/etc/clamav/2. 設定ファイルを生成する (ハマりポイント)
freshclam.conf.sample をコピーするだけでは動かない。サンプルの 8 行目に Example という単語だけの行があり、これは「設定ファイルがそのままサンプルだから読み込みを拒否する」というセーフティ機構。コメントアウトしないと freshclam がエラーを吐く。
ついでに、デフォルトの DB / ログ パスは Linux 前提で /var/lib/clamav と /var/log/freshclam.log を指している。macOS では Homebrew 配下に揃えたほうが整理できる。
# サンプルをコピー
cp /opt/homebrew/etc/clamav/freshclam.conf.sample \
/opt/homebrew/etc/clamav/freshclam.conf
# Example 行をコメントアウト & パスを Homebrew 配下に
sed -i '' \
-e 's/^Example$/#Example/' \
-e 's|^#DatabaseDirectory /var/lib/clamav|DatabaseDirectory /opt/homebrew/var/lib/clamav|' \
-e 's|^#UpdateLogFile /var/log/freshclam.log|UpdateLogFile /opt/homebrew/var/log/freshclam.log|' \
-e 's|^#LogTime yes|LogTime yes|' \
/opt/homebrew/etc/clamav/freshclam.conf
# 受け皿ディレクトリを作る (これを忘れると Permission denied)
mkdir -p /opt/homebrew/var/lib/clamav /opt/homebrew/var/logdiff で見るとサンプルからの変更はこれだけ。
< Example
> #Example
< #DatabaseDirectory /var/lib/clamav
> DatabaseDirectory /opt/homebrew/var/lib/clamav
< #UpdateLogFile /var/log/freshclam.log
> UpdateLogFile /opt/homebrew/var/log/freshclam.log
< #LogTime yes
> LogTime yes
3. 定義 DB の取得 — freshclam (実測 10 秒)
freshclam出力 (一部抜粋)。
ClamAV update process started at Sun May 10 22:05:34 2026
daily database available for download (remote version: 27996)
ERROR: NULL X509 store
ERROR: NULL X509 store
Testing database: '.../tmp.../daily.cvd' ...
Database test passed.
daily.cvd updated (version: 27996, sigs: 355446, f-level: 90, ...)
main database available for download (remote version: 63)
ERROR: NULL X509 store
ERROR: NULL X509 store
main.cvd updated (version: 63, sigs: 3287027, ...)
bytecode database available for download (remote version: 339)
ERROR: NULL X509 store
ERROR: NULL X509 store
bytecode.cvd updated (version: 339, sigs: 80, ...)
freshclam 4.58s user 0.95s system 52% cpu 10.504 total落ちてきた DB はこの通り。
| ファイル | サイズ | シグネチャ数 |
|---|---|---|
main.cvd | 85 MB | 3,287,027 |
daily.cvd | 22 MB | 355,446 |
bytecode.cvd | 275 KB | 80 |
合計 約 107 MB / 約 364 万シグネチャ。これに加えて *.cvd.sign 署名ファイルが 9 KB ずつ。
ERROR: NULL X509 store の正体
ClamAV 1.5 系から CVD に X.509 証明書ベースの追加検証が入ったが、macOS ビルド (Homebrew bottle) では証明書ストアの初期化に失敗してこの ERROR が出る のが既知挙動。
Database test passed. が直後に出ていて DB 自体の SHA / FP 検証は通っているので、実害はない (*.cvd は別経路で改ざん検知されている)。
ただしログに ERROR の文字列が並ぶので、CI 等で固定文字列を grep していると false alarm になる。手動で気にしなくていいが、自動監視のフィルタはこのパターンを除外しておくと良い。
4. 動作確認に EICAR テストファイルを置く
EICAR (European Institute for Computer Antivirus Research) のテスト文字列は AV ベンダ全社が「無害だが必ず検出する」という運用合意のあるシグネチャ。本物のマルウェアを使わずに動作確認できる。
mkdir -p ~/clamav-test
printf 'X5O!P%%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' \
> ~/clamav-test/eicar.com
clamscan ~/clamav-test/eicar.com/Users/user/clamav-test/eicar.com: Eicar-Test-Signature FOUND
----------- SCAN SUMMARY -----------
Known viruses: 3627854
Engine version: 1.5.2
Scanned files: 1
Infected files: 1
Data scanned: 68 B
Time: 7.760 sec (0 m 7 s)68 バイトのファイル 1 件で 7.76 秒。これがほぼ全部 エンジン起動 + DB ロード時間。
スキャン本体ではなく初期化が重いので、たくさんの呼び出しを直列に走らせるユースケースでは clamd (常駐デーモン) + clamdscan を使ったほうが圧倒的に速い。今回は単発のフルスキャンが目的なので clamscan のままで進める。
5. ホームディレクトリのフルスキャン
du -sh ~ で見ると 286 GB。この内訳が問題で、
117G /Users/user/Parallels # 仮想マシンのディスクイメージ
102G /Users/user/Library # アプリ データ / キャッシュ / コンテナ
13G /Users/user/Documents
8.7G /Users/user/.cache
7G /Users/user/go
6.7G /Users/user/.ollamaParallels の VM ディスク (117 GB) と Library/Caches・Library/Containers 系を除外 することにした。理由は次の3つ。
- VM ディスクは
.hddバンドル内部にゲスト OS の全データが入っており、ClamAV はそこを再帰的に展開してスキャンしようとする。これは「自分の Mac をスキャンしたい」というスコープから完全に外れる Library/Containers配下は macOS の Sandbox/TCC 制御対象 で、フルディスクアクセス権限をclamscanプロセスに付与していない限りPermission deniedの山になる。出力ノイズが膨大Library/Cachesは内容が常に揺れる。スキャン中に消えるファイルが大量に発生し、Can't open file警告が大量に出る
商用 AV 製品 (Sophos, ESET 等) は Apple の Endpoint Security フレームワークで権限を持つので Container の中も読めるが、Homebrew 版 ClamAV は普通のユーザ権限で動く。tccutil で許可を渡すこともできるが、本記事では「素の状態」を優先した。
実行コマンド。
nohup clamscan -r -i \
--max-filesize=100M \
--max-scansize=400M \
--exclude-dir='^/Users/user/Parallels' \
--exclude-dir='^/Users/user/Library/Caches' \
--exclude-dir='^/Users/user/Library/Containers' \
--exclude-dir='^/Users/user/Library/Group Containers' \
/Users/user > ~/clamav-test/logs/home-scan.log 2>&1 &オプションの意味。
| オプション | 意味 |
|---|---|
-r | 再帰的にディレクトリを下る |
-i | 感染ファイルのみ表示 (クリーン ファイルのログを抑制) |
--max-filesize=100M | 100MB を超えるファイルはスキップ。VM ディスクや動画など |
--max-scansize=400M | 1ファイルから展開される総量がこれを超えたら打ち切る (圧縮爆弾対策) |
--exclude-dir='^...' | パス先頭一致で除外 |
実行中の様子。
PID 55770, 98% CPU (single-thread)
RSS 341 MB → 220 MB (DB ロード後はメモリ常駐)ClamAV は シングルスレッドのスキャナ。マルチコアの恩恵を得るには clamdscan で並列化するか、clamscan を複数同時起動して対象を分割するしかない。
スキャン結果 — 74 分でディスク フル落ち
ここが今回いちばん書きたかったところで、SCAN SUMMARY が出ないまま終わった。プロセスは生きていたまま clamscan 自身がエラーを吐き続けて止まった、というのが正確な顛末。
ログ末尾はこんな感じ。
LibClamAV Error: cli_gentempfd_with_prefix: Can't create temporary file
/var/folders/c2/.../T//clamav-46bbf1b1...tmp: No space left on device
LibClamAV Error: cli_gentempfd_with_prefix: Can't create temporary file
/var/folders/c2/.../T//clamav-e26d2bb7...tmp: No space left on device
... (以下、`No space left on device` が延々と続く)
LibClamAV Warning: cli_unzip: failed to create temporary file ...スキャン開始前のディスク空きが 3.2 GB しかなかったのが原因。
ClamAV はアーカイブ (zip / xz / tar / Office 文書 / .ipa / .apk / Mach-O Universal Binary など) を $TMPDIR 配下に展開してからスキャンする。286 GB のホームを舐めると、Go モジュール キャッシュ・npm キャッシュ・Xcode の中間ビルド成果物・Docker レイヤなど 無数の中規模アーカイブが順に展開され、温度の温まったテンポラリで空きが押し切られた。
ClamAV 本体のメモリは ~200 MB で問題なかったが、$TMPDIR の使い方は派手なので Mac だと特に注意。回避策は次のいずれか。
clamscan --tempdir=/path/to/spacious/volumeで別ボリュームを指定 (外付け SSD など)--scan-archive=noでアーカイブ展開を一切やらない (速いが、暗号化・難読化済みアーカイブ内のマルウェアを見逃す)--max-files=N--max-recursion=Nで深さ制限- スキャン前に
du -sh $TMPDIRを確認、最低でも 20 GB 以上の空きを確保
実際に検出された 7 件
74 分の間に拾われたヒットはこの 7 件。
~/go/pkg/mod/cache/download/github.com/klauspost/compress/@v/v1.18.2.zip:
Heuristics.Zip.OverlappingFiles FOUND
~/go/pkg/mod/cache/download/github.com/klauspost/compress/@v/v1.18.3.zip:
Heuristics.Zip.OverlappingFiles FOUND
~/go/pkg/mod/github.com/klauspost/compress@v1.18.2/zip/testdata/FuzzReader-raw.zip:
Heuristics.Zip.OverlappingFiles FOUND
~/go/pkg/mod/github.com/klauspost/compress@v1.18.3/zip/testdata/FuzzReader-raw.zip:
Heuristics.Zip.OverlappingFiles FOUND
~/go/pkg/mod/cache/download/github.com/vulncheck-oss/go-exploit/@v/v1.51.0.zip:
Txt.Backdoor.MetasploitPayload-9874938-0 FOUND
~/go/pkg/mod/github.com/vulncheck-oss/go-exploit@v1.51.0/payload/bindshell/bindshell_test.go:
Txt.Backdoor.MetasploitPayload-9874938-0 FOUND
~/clamav-test/eicar.com:
Eicar-Test-Signature FOUNDカテゴリ別に意味を読むと、
- Eicar-Test-Signature (1件): 自分で置いたテスト ファイル。期待通り
- Heuristics.Zip.OverlappingFiles (4件):
klauspost/compress(Go の zip 高速実装) のテスト フィクスチャ。ヒューリスティックであって YARA / 既知ハッシュ ベースの検出ではない。「ZIP のローカル ヘッダがオーバラップしている = ZIP 仕様の隙を突いた構造」という、まさにklauspost/compressがフェイルセーフ実装をテストしたい対象そのもの。完全に正当 - Txt.Backdoor.MetasploitPayload-9874938-0 (2件):
vulncheck-oss/go-exploitという 正規の脆弱性検証ライブラリ に含まれる Metasploit ペイロード サンプル文字列を検出。シグネチャ ベースなので、これも当然のヒット
つまり 7 件すべて誤検出 (false positive) または意図したヒット。逆に言うと、開発者の Mac には 「シグネチャに引っかかるが正当な研究 / テスト データ」が普通に入っている ということで、自動隔離 (--move, --remove) は絶対に避けたほうがいい。手動で 1 件ずつパスを見て判断する運用が現実解。
途中で出たエラー / ワーニングの内訳
完走しなかったとはいえ、ログ全 537 行の内訳は次のとおり。
| 件数 | パターン | 意味 |
|---|---|---|
| 430 | index_local_file_headers_within_bounds | ZIP として認識した小さなファイルがマルフォーム。Go モジュール キャッシュの *.ziphash 等の小ファイルが疑似的に ZIP マジックを持っているのが原因 |
| 77 | cli_unzip: failed to create temporary file | テンポラリ作成失敗 (上記のディスク フルが原因) |
| 16 | cli_gentempfd_with_prefix: ... No space left on device | 同上 (より低レイヤの直接的なエラー) |
| 4 | cli_scanxz: premature end of compressed stream | 切り詰められた xz アーカイブ。スナップショット途中のキャッシュ ファイル等で発生しがち |
| 1 | cli_writen | 書込失敗 (これもディスク起因) |
index_local_file_headers_within_bounds の 430 件はディスク関係ない純粋な誤検知ログで、Go や Node エコシステムを使う Mac なら必ず出る。「ClamAV の Error は無視可能なものが含まれる」 という事実をまず受け入れる必要がある。
結局スキャンの「結果」はどうなのか
SCAN SUMMARY ブロックは出ていないので、通常の意味でのスキャン完了ではない。ただし、
- 開始:
2026-05-10 22:10:32 - 異常終了:
2026-05-10 23:24:53(ログ最終更新) - 経過: 74 分 21 秒
- 検出 (FOUND): 7 件すべて誤検出 / 意図したヒット
- ピーク CPU: 99% (シングルスレッド)
- ピーク RSS: 約 340 MB → 後半は 120 MB 前後で安定
- ホーム実スキャン量: 286 GB のうち、Parallels と Library/{Caches,Containers,Group Containers} を除いた約 67 GB を 7 割程度処理した時点で停止 (推定)
6. 詰まりどころ早見表
| 症状 | 原因 | 対処 |
|---|---|---|
freshclam: ERROR: Please edit the example config file before running freshclam! | サンプルそのままの Example 行が残っている | 8 行目をコメントアウト |
freshclam: Can't create new file ...: Permission denied | DB ディレクトリが存在しないか書込権限がない | mkdir -p してオーナーを自分に |
ERROR: NULL X509 store (連発) | macOS bottle の証明書ストア初期化失敗 (CVD 検証は別経路で通る) | 無視可。CI フィルタからは除外 |
clamscan: ... Permission denied | macOS の TCC / Sandbox や Library/Containers 権限 | --exclude-dir で除外。深く読みたい場合は tccutil でフルディスクアクセスを付与 |
| スキャンが終わらない | VM ディスクや巨大ファイルを再帰展開している | --max-filesize / --max-scansize / --exclude-dir |
cli_gentempfd_with_prefix: No space left on device | $TMPDIR の空きを使い切った (アーカイブ展開で膨張) | --tempdir= で大きいボリュームを指定。最低 20 GB は確保 |
index_local_file_headers_within_bounds: Invalid offset arguments 大量発生 | 小さい非 ZIP ファイルを ZIP と誤認している | 無視可。Go / npm キャッシュを抱えると数百件出る |
cli_scanxz: premature end of compressed stream | 切り詰められた xz | 無視可 |
| 単一ファイル スキャンに 7 秒 | DB ロードに時間がかかっている | clamd + clamdscan で常駐させる |
7. 常用するなら clamd 化を検討
毎回 ~7 秒のロードが入る clamscan は単発向け。常用したいなら clamd をデーモンで動かして clamdscan を叩く のがセオリー。Homebrew なら、
sudo brew services start clamavこれで clamd がポート (UNIX socket) で待ち受けるので、clamdscan <path> 一発で 1 秒以内に結果が返ってくるようになる。
ただし clamd.conf も同じく Example 行のコメントアウトと TCPSocket / LocalSocket の設定が必要。
まとめ
- インストールは 14 秒、DB 取得は 10 秒。早い
- 真の難所は
freshclam.confのExample行と DB / ログ パスのデフォルト値。Linux 前提なので macOS では必ず置換する ERROR: NULL X509 storeは無視してよい既知挙動 (ClamAV 1.5 系 / Homebrew bottle)- 「ホーム フルスキャン」 =
~/全部、ではない。Mac ではLibrary/Containers・Library/Caches・Parallelsを除外しないと、ノイズで本当に怪しいヒットが埋もれる $TMPDIR空きが命取り。今回 3.2 GB で始めて 74 分で ENOSPC 落ち。アーカイブ展開分を含めて 20 GB 以上の空きと--tempdir指定 が現実解- 検出ヒットは「正当データ」が混ざる。開発者 Mac では Go / Node / セキュリティ研究系のテスト データがシグネチャに引っかかる。
--remove/--moveの自動隔離は危険 - シングルスレッドで重い。常用するなら
clamd化する