shutil.make_archive()が止まらない
事の発端
最近、自分の作った画像解析ソフトウェアをStreamlitでWebアプリケーション化して、研究所のプログラミングに明るくないメンバーに使ってもらっています。Streamlitは(簡単なものなら)かなり簡単に作れるため、プロトタイピングに大変便利ですね!しかし、今日ユーザーから、いつまで経っても処理が終わらない、という報告を受けました。ログを見ると、shutil.make_archive()
がNo space left on device
とだけ言い残して死んでいます。あれ、ホストしてるマシンはSSDのスペースまだかなり余っていたはずだけど、と思ってdf
コマンドを叩いてみると、/
以下の使用率が100%近くなっていました。
何故か
A: shutil.make_archive('test/out', 'zip', root_dir='test')
のように、アーカイブ対象のディレクトリ内でZIPファイルを作ったせいで、再帰的にZIPファイルが巨大化してしまった。
Streamlitを使ったウェブアプリでは、解析結果はtempfile.TemporaryDirectory()
で作成した一時ディレクトリ内に保存し、それを最後にzipファイルにまとめてダウンロードする仕組みにしています。ここで、うっかり上記のように、root_dir
内にzipファイルを作ることで、デバイスの容量を圧迫してプログラムが死ぬまでzipファイルが膨れ上がる現象が生じてしまったようです。
ちなみに、Unixのzipコマンドで同様の事(zip -r test/out.zip test
)を行っても、test/
内にout.zip
が常識的なファイルサイズで作成されます。今回の一件はかなりしょぼいミスが原因(なぜテストで気が付かなかったのか?)でしたが、その代償が大きすぎるというか、zip爆弾のように何らかの脆弱性にもつながりそうだと思いました。
再現コード
.
├── test
│ └── large_image.png
└── test.py
❯ python -V
Python 3.10.14
large_image.png
には、zipファイルの膨張が実感できるように、それなりに大きいファイルを入れておきます。
# test.py
import shutil
shutil.make_archive("test/test", "zip", "test")
❯ python test.py
以下のように、順調に(?)ファイルサイズが大きくなっていくのがわかります。
❯ du
142704 ./test
142708 .
❯ du
326996 ./test
327000 .
❯ du
559856 ./test
559860 .
❯ du
950052 ./test
950056 .