プロジェクト

全般

プロフィール

Bug(バグ) #1908

Installing some plugins were failed with PHP 5.3.4+ (PHP 5.3.4 以上でプラグインの展開に失敗することがある)

isao sano約8年前に追加. ほぼ8年前に更新.

ステータス:
Fixed(完了)
優先度:
Normal(通常)
担当者:
対象バージョン:
開始日:
2011-02-07
期日:
進捗率:

100%

3.6 で発生するか:
3.8 で発生するか:
Unknown (未調査)

説明

Overview (現象)

Executing opPlugin:install was failed with the following error.

opPlugin:install の実行が以下のエラーによって失敗することがある。

Installation of "opAuthMobileUIDPlugin" plugin failed: unable to unpack /path/to/OpenPNE3/cache/.pear/opAuthMobileUIDPlugin-1.0.0.tgz

Causes (原因)

Executing opPlugin:install was failed on extracting a long-named file in plugin package.

Tar can't put >= 100 bytes filename in a header (this limitation comes from the specification). So some derived tar specs are able to do it for using special ways.

Archive_Tar follows GNU tar specification. This specification allows to store filename in fixed length content part. Content of that part must be 512 bytes, so archivers cover the deficits by using NULL.

Archive_Tar 1.3.2, which is used by OpenPNE 3, doesn't consider NULL after the long filename. It try to open file handler for writing filename with NULL by using fopen(). On PHP 5.3.3 and before, fopen() recognizes that end of the filename is first NULL, as a result, that file was written correctly. However on PHP 5.3.4 and after, failed to open that file and failed to extract that file because that version of PHP has the following changes for preventing NULL-byte attack.

http://svn.php.net/viewvc?view=revision&revision=305507

プラグインパッケージ内の長い名前を持ったファイルを展開する場面で opPlugin:install の実行が失敗する。

tar は仕様上の制限によりヘッダに 100 バイト以上のファイル名を格納することができない。そのため、派生した tar の仕様では特別な方法によりそれを可能にしているものがある。

Archive_Tar は GNU tar の仕様に従っている。この仕様は固定長のコンテンツ部にファイル名を格納することを可能にしているものである。このパートの内容は 512 バイトでなければならないため、アーカイバは NULL を用いることで不足を埋めている。

OpenPNE 3 で用いている Archive_Tar 1.3.2 は後続の NULL を考慮しておらず、 fopen() で NULL バイト付きファイル名の書き込み用にファイルハンドラをオープンしようとする。 PHP 5.3.3 以前では最初の NULL がファイル名の終端としてみなされ、結果的には Archive_Tar の想定通りにファイルが書き込まれる。ところが、 PHP 5.3.4 からは NULL byte attack の対策として以下の変更がおこなわれているため、ファイルのオープンがおこなえず、そのファイルの抽出に失敗してしまう。

http://svn.php.net/viewvc?view=revision&revision=305507

Way to fix (修正内容)

関連情報

20110418_patch_for_Archive_Tar.diff 表示 (4.44 KB) Kousuke Ebihara, 2011-04-18 17:26


関連するチケット

関連している OpenPNE 3 - Backport(バックポート) #1996: Installing some plugins were failed with PHP 5.3.4+ (PHP 5.3.4 以上でプラグインの展開に失敗することがある) Fixed(完了) 2011-02-07
関連している OpenPNE 3 - Backport(バックポート) #1997: Installing some plugins were failed with PHP 5.3.4+ (PHP 5.3.4 以上でプラグインの展開に失敗することがある) Fixed(完了) 2011-02-07
関連している OpenPNE 3 - Backport(バックポート) #1998: Installing some plugins were failed with PHP 5.3.4+ (PHP 5.3.4 以上でプラグインの展開に失敗することがある) Fixed(完了) 2011-02-07
関連している OpenPNE 3 - Backport(バックポート) #1999: Installing some plugins were failed with PHP 5.3.4+ (PHP 5.3.4 以上でプラグインの展開に失敗することがある) Fixed(完了) 2011-02-07
関連している OpenPNE 3 - Bug(バグ) #3918: PHP 5.5 以降でプラグインの展開に失敗する Won't fix(対応せず) 2016-03-14

関係しているリビジョン

リビジョン 426023bf (差分)
Kousuke Ebiharaほぼ8年前に追加

updated PEAR::Archive_Tar to 1.3.7 for avoiding error on extracting long-named file with PHP 5.3.4+ (fixes #1908)

履歴

#1 Kousuke Ebiharaほぼ8年前に更新

ざっくり調べたので、原因についての現時点の見解を以下に示します。

展開できなかったファイルを調べたところ、いずれのファイルもパス名を含めて 100 バイトを超えたものとなっていました。 tar の規格上、ヘッダ部に格納可能なファイル名は 100 バイトまでと決まっているため、このようなファイルを格納することは通常できません。

tar の GNU 拡張では、この問題に対して、 100 バイトを超える、本来格納不可能なファイル名を、ヘッダ部ではなくコンテンツ部に格納することで対処しています。 PEAR の Archive_Tar によって作成されるパッケージもこの方式を採用しており、読み込みと書き込みの両方で、 GNU 形式の 100 バイトを超えたファイル名を扱えるようになっています。

ただし、 Archive_Tar には読み込みのタイミングにおける、 100 バイトを超えたファイル名の取得方法に誤りがあり、以下のチケットで(Archive_Tar 1.3.3 で)修正されています。

Bug #11594 :: _readLongHeader leaves 0 bytes in filename
http://pear.php.net/bugs/bug.php?id=11594

tar は 512 バイト単位のブロックがいくつも組み合わさることにより成り立っています。ひとつのファイルにつき、最初に見つかったブロックはヘッダ部として扱われ、以降のブロックはコンテンツ部として扱われます。ヘッダやコンテンツの内容だけではそのブロックを埋められない場合、残りは NULL で埋められることになります。

OpenPNE で使用している Archive_Tar 1.3.2 は、ヘッダから取得可能な(短い)ファイル名については、終端に NULL が含まれていることを考慮し、 trim() をおこなっていましたが、コンテンツ部から取得する(長い)ファイル名については、終端の NULL の考慮をおこなっていませんでした。 Archive_Tar 1.3.3 では、この問題について、先に上げたチケットにて trim() をおこなうようにして対応しているようです。

ここからは推測になりますが、 NULL 文字が含まれるファイル名であったとしても、 PHP 5.3.3 までは問題なく扱うことができていたのではないかと思います。つまり、おそらく、この現象は PHP 5.3.4 以降で発生するものであるということになります。

というのも、 PHP 5.3.4 では、以下の変更がおこなわれているからです。

http://svn.php.net/viewvc?view=revision&revision=305507

この変更については、 Rasmus Lerdorf による以下のポストから知ることができます。

php.internals: Adding path_len to all stream functions in trunk
http://news.php.net/php.internals/50191

この変更により、ファイルパスを受け付ける多くの関数の引数は NULL 文字を含む文字列を許容しなくなりました。たとえば fopen() では、 NULL 文字が含まれている(=PHP の string 型変数としての文字列長と、 C 言語の char 型変数としての文字列長が一致しない)場合に false を返して失敗するようになりました。

tar の展開に使われる Archive_Tar::_extractList() では、 tar アーカイブから取得できたファイル名を fopen() 関数によってオープンし、ファイルの書き込みをおこなおうとします。つまり、ファイル名として NULL 文字を許容しなくなった PHP 5.3.4 以降で Archive_Tar 1.3.2 を使用し、パス名が 100 文字以上になるファイルを含むアーカイブを展開しようとすると、展開先のファイル名に NULL 文字が含まれていることにより fopen() が失敗し、 Archive_Tar はそこで展開を中断してしまいます。

これが本現象の原因なのではないかと思われます。仮にこの見立てが正しかったとすると、本現象の回避策は(利用者側で同梱の Archive_Tar を 1.3.3 以降のものに置き換えるか、プラグイン側で 100 文字以上のパスを使わないようにする以外に)ないということになります。

#2 Kousuke Ebiharaほぼ8年前に更新

  • 対象バージョンOpenPNE 3.7.0 にセット

#3 Kousuke Ebiharaほぼ8年前に更新

  • ステータスNew(新規) から Accepted(着手) に変更
  • 担当者Kousuke Ebihara にセット

#4 Kousuke Ebiharaほぼ8年前に更新

Archive_Tar 1.3.2 から最新の 1.3.7 までの変更点の概要と、それぞれについて OpenPNE への適用が必須かどうかの判断をしたものを以下に示します。

  • r227940 : bzip2 取り扱い部分についてのみの変更。 PEAR パッケージとして有効なアーカイブは無圧縮 tar か gzip により圧縮された tar に限られるため、この変更を適用する必要はない。
  • r241532 : 今回問題になっている、長いパス名の扱いが間違っている問題の修正であり、変更を適用する必要がある。
  • r241545 : アーカイブ作成時と展開時でシンボリックリンクの考慮がなされていなかった。展開時の変更は微量であり、現在、シンボリックリンクを使用しているプラグインは存在しないため、即座に変更を入れる必要はない。
  • r268047 : ライセンス変更。このリビジョン以後の変更は修正 BSD ライセンスに基づいて扱われなければならないため、適用の必要がある。
  • r287099 : アーカイブ作成過程において、変更時刻を表すヘッダに使う値が正しくないのを修正したというもの。 OpenPNE を動作させるにあたり、適用が必須なものではない。
  • r287105 : GNU tar として展開プログラムが認識するためのマジックヘッダがないというもの。プラグインパッケージは Archive_Tar で展開することを想定しているため、この変更を適用しなくとも OpenPNE の動作に影響はない。
  • r287108 : 512 バイト以上の (ブロックをまたがる) パス名の途中に NULL が含まれてしまうというもの。いまのところ、プラグインでこの規模のパス名を生成するアーカイブは存在しないと思われるため、現時点で修正が必須なものではないが、パス名がライブラリ等の制約によって必要以上に (512 バイト以上に達するほどに) 長くなるという可能性は否定できないため、念のためこの変更も適用する。
  • r287467 : 展開時にパス名前後のスペースが除去されてしまう。このようなファイル名を意図してつけることはないとは思うが、なんらかの手違いによりそのようなファイルが混入している可能性はありうるので、この修正は入れたほうがよい。
  • r287963 : いままで一部のエラー時に die() をコールしてスクリプトを強制的に終了させていたのを、 PEAR の標準的なエラーハンドリングに従うように変更したもの。適用が必須なものではない。
  • r293090 : ファイルの走査方法を変更し、 2GB 以上のファイルを取り扱えるように変更したもの。適用が必須なものではない。
  • r295988 : 無視ルールに当てはまるファイルをアーカイブしないようにする機能追加。適用が必須なものではない。

以上の見解にしたがい、 r241532 と r268047 と r287108 と r287467 の変更のみを適用した際の差分を添付しました。ライセンス以外はいずれも軽微かつ最低限の変更であり、緊急リリースの差分としては妥当なところだと思います。

http://redmine.openpne.jp/attachments/267/20110418_patch_for_Archive_Tar.diff

そこで、以下のような対応をおこなうことにします。

  • 3.0, 3.2, 3.4 向けにはこの差分を適用したものをリリースする
  • 開発版である 3.6 と 3.7 (master) では、 PEAR::Archive_Tar に対するバグ修正の恩恵を完全に享受できるよう、 Archive_Tar 1.3.7 へのバージョンアップをおこなうことにする

#5 Kousuke Ebiharaほぼ8年前に更新

  • ステータスAccepted(着手) から Pending Review(レビュー待ち) に変更
  • 進捗率0 から 50 に変更

更新履歴 426023bfe32c9f45edbedb28af2e7a4ce2769c64 で適用されました。

#6 Kousuke Ebiharaほぼ8年前に更新

  • 題名プラグインのtgzファイル名に不要なスペースがつくことがある から Installing some plugins were failed with PHP 5.3.4+ (PHP 5.3.4 以上でプラグインの展開に失敗することがある) に変更

#7 Rimpei Ogawaほぼ8年前に更新

  • ステータスPending Review(レビュー待ち) から Pending Testing(テスト待ち) に変更
  • 進捗率50 から 70 に変更

master の変更確認OKです。

#8 Kiwa Sakaiほぼ8年前に更新

  • ステータスPending Testing(テスト待ち) から Fixed(完了) に変更
  • 進捗率70 から 100 に変更
  • PHP5.3.4
  • PHP5.3.6
  • PHP5.2.17(PNE3.0のみ5.2.8)
  • PHP5.2.3

上記バージョンで確認したところ、プラグインのダウンロードの処理に問題はなく、PHP5.2.3以外のバージョンでは正常にインストールできることを確認しました。
PHP5.2.3 については マニュアルにある推奨バージョンの最低のものということで確認を行いましたが、OpenPNE3.6以上についてはインストール開始直後セグメンテーション違反になりました。

>> installer start clean install
セグメンテーション違反です

この問題はこのチケットでの対応以前から発生しており(OpenPNE3.6beta8で確認)、またPHP5.2.4でも同現象が発生していることが確認できたので、別チケットを作成しました。

#2011: php5.2.3・php5.2.4 で openpne:install をおこなうとセグメンテーション違反でインストールに失敗する

#9 kaoru nishizoeほぼ3年前に更新

  • 関連している Bug(バグ) #3918: PHP 5.5 以降でプラグインの展開に失敗する を追加

他の形式にエクスポート: Atom PDF