Created: 2001-11-05@666
Updated: 2001-11-11@662

問題の概要

iTunes 2.0 for Mac OS Xのインストールパッケージを使用すると、特定の条件で起動ボリューム以外のボリュームのデータが消去されてしまう可能性がある。インストールパッケージに含まれる、古いiTunesを消すためのスクリプトが原因となっている模様。問題のスクリプトのファイル名は“preflight”と“VolumeCheck”である。“preflight”は以下の内容となっている。

#!/bin/sh # if iTunes application currently exists, delete it if [ -e $2Applications/iTunes.app ] ; then rm -rf $2Applications/iTunes.app 2> /dev/null fi exit 0

“$2”に空白を含んだパスが指定される場合に問題が発生する可能性がある。

次に、“VolumeCheck”は以下の内容となっている。

#!/bin/sh # if current iTunes pkg exists, delete it b/c of Installer bug if [ -e "$1Library/Receipts/iTunes.pkg" ] ; then rm -rf "$1Library/Receipts/iTunes.pkg" 2> /dev/null fi # return do nothing status to Installer exit 0

こちらは“$1”に空白を含んだパスが指定される場合に問題が発生する可能性がある。

関連記事へのリンク

問題が確認されたMac OS Xのバージョン

Mac OS X 10.1

解決方法

iTunes 2.0.1 for Mac OS Xインストーラーを用いる。iTunes 2.0 for Mac OS Xのインストーラーが手元に残っている場合は、将来誤って使用してしまわないよう、消去すること。

状況の経過

2001-11-11(その2)

ポセイドンのページで、問題が起こる条件について、さらに検証を行っている。これまでは起動ボリューム以外のボリュームのボリューム名に空白が含まれていることが前提であったが、空白が含まれていなくても、問題が発生する可能性があるとのこと。

ただし、iTunes 2.0.1 for Mac OS Xインストーラーを使えば解決されるのは同じであり、問題の根本原因はシェルスクリプトプログラミングのミスであることに変わりはない。

2001-11-11(その1)

Mac OS X 10.1に対するソフトウェアアップデートである、Installer Update 1.0は、今回の問題の原因となるようなスクリプトの挙動を防ぐものではないということが、ポセイドンのページで検証されている。

2001-11-07

ポセイドンのページでiTunes 2.0 for Mac OS Xのインストールパッケージが問題を起こす条件を分析している。"preflight"以外にも"VolumeCheck"というスクリプトも同様の問題を抱えている。したがって、インストーラに含まれる「iTunes for Mac OS X 情報.rtf」に書かれている古いiTunes野アプリケーションファイルを待避していても、問題が発生する可能性がある。従って、2001-11-04(その1)の該当記述は撤回させていただく(ただし今回の問題とは関係なく、インストール前に関連ドキュメントに目を通すことを推奨したい)。

さらにポセイドンのページでは分析結果として問題の発生条件を以下のように述べている。

したがって、発生条件は以下のようになる。

(1) マウントしているボリュームの中に、名称に空白を含むボリューム(A)がある。
(2) ボリューム(A)の名称の空白以前の文字列と一致する名称の別のボリューム(B)が存在する。
またはボリューム(A)の名称の先頭が空白文字である。

現象としては、以下のようになる。

(1) ボリューム(A)の名称の空白以前の文字列と一致する名称の別のボリューム(B)が存在する場合、ボリューム(B)の内容が削除される。
(2) ボリューム(A)の名称の先頭が空白文字の場合、マウントしている起動ディスク以外のボリュームの内容が削除される。

2001-11-04(その2)

もう少し件のiTunes2.0のスクリプトについて考えてみると…。"$2"の部分に何が入るのかが気になる…。デベロッパのドキュメントにも記述がかなったの推測ばかりになるけど、でも、/Volumesには起動ボリュームは追加されないので、起動ボリューム自身が誤って消されることはないはずだ。

となると、iTunesを起動ボリューム以外にインストールしていた、という条件が加わることになる。ここで、"$2"にどのような値がどのような条件ではいるのかが判れば、問題が起こる場合と起こらない場合がはっきりする。

しかも、これまでの情報から、ボリューム名やフォルダ名に空白が入っていただけでは発生しない。空白で分割されて、それが運悪くボリューム名とぶつかってしまう、という条件が重なるのだ。一番最悪の条件なのが先頭が空白のボリューム名を含むパスが"$2"が入ってくるときで、このときは起動ボリューム以外のボリュームが消されてしまう。

まー、条件が非常にレアでも、揃えば確実に起こってしまうことなので、その5で書いた開発者側の配慮は同じ。別にこれはUNIXな世界でも許されることではないので、shを使う場合の一般的な配慮と考えることが出来る。というか、「ユーザの入力した値の可能性を徹底的に検証する」というのは、セキュリティの世界では一般常識だ。今回の場合は、「ボリューム名」というユーザがいじる可能性のある文字列を疑う、というのが抜けていたということ。それもごく初歩的な部分で。

(補足:「別にこれはUNIXな世界でも許されることではない」の部分について。近代的ないわゆるUNIXなOSでも、特にファイル名やフォルダ名に空白を含むことはできない、などという制限はない。だから、シェルプログラミングをするときは空白が含まれる可能性があることを前提にしなければいけないのだ。ただ、使用頻度の多いシェルのコマンドラインインタフェースではスペースをエスケープしたりクォートで囲んでやったりしないといけないので、ユーザは自然と空白を避けるようになる。URLでも"%20"で空白は表現できるので、httpサーバなんかでも特に空白を含むパスを回避する必然はないのだ。今回の件でも「UNIXだから制限が…」などという意見を持ち出す向きもあるようだが、それは的外れなように思う。あくまでシェルスクリプトプログラミングの基本ミスに還元される。プログラミングミスでデータが危機にさらされる可能性があるのは、どのOSにもありえることだ。)

2001-11-04(その1)

Tales about Appleさんのところで、iTunes2のインストールスクリプトの解析が行われていた。問題の箇所は、「Applicationフォルダに古いiTunesが残っていたら、消す」という処理をしていて、そこで実際に古いiTunesを消す部分に問題があった、ということのようだ。

そこで思い出したのだけど、iTunes2のReadMeである「iTunes for Mac OS X 情報.rtf」には、インストールする際の注意として次のようなことが書いてあった(これは2.0のインストーラも2.0.1のインストーラも一緒)。

iTunes for Mac OS X をインストールする

これまでに「iTunes for Mac OS X」をインストールしている場合は、新しいバージョンをインストールする前に、古いバージョンを削除してください。「iTunes」を「Dock」に置く場合は、まず「Dock」から古い「iTunes」のアイコンを取り除き、新しい「iTunes」をインストールした後で、新しい「iTunes」のアイコンを「Dock」にドラッグします。

自分の場合はこの記述通り、古いiTunesをゴミ箱に入れてからインストールを行ったので、今回の問題からは無関係だったようだ。つまり、古いiTunesをあらかじめApplicationフォルダから取り除いてあるので、問題のスクリプトが実行されることはなかったのだ(実行されても自分の環境であれば問題は起きなかったのだが…)。

今回問題に遭遇した人は、上記の処置をせず、なおかつボリューム名の先頭を半角のスペースにしていたりしていた、という条件が当てはまるようだ。

ReadMeの通りにユーザが作業していれば…という条件ではテスト出来ていたのかもしれない。だけど、そうではない場合の対処が出来ていなかったようだ。「ReadMeを読んでいないのが悪い」という指摘も出来るけど、そういったマニュアル類を読まないことを想定して最悪の事態を避けることはメーカーの義務だ。その点で、今回はAppleは失敗してしまったということかな。

ユーザサイドとしては、自分の身を自分で守るためにも、ちゃんとインストール関連のドキュメントには目を通すこと、というのが教訓。

Mac OS X上のソフトウェア開発者の視点からも考えておく。今回の件では、Installer.app自体の問題でもなく、そのInstallerから呼び出されるスクリプトの問題であるので、すべてのMac OS X開発者に関連することだ。shを使って何か作業をさせるのなら、パスの指定を必ずダブルクォート等で囲んでおく、という教訓になる。

© ぴぐもん, 2001