パスワードを忘れた? アカウント作成
12032040 journal
日記

taka2の日記: Wordpressのバグに遭遇 3

日記 by taka2

Wordpress 4.2 が出ていたので更新したら、以後、プラグインの更新が動かなくなりました。

エラーログを見ると、

PHP Fatal error:  Maximum function nesting level of '100' reached, aborting! in …/wp-admin/includes/file.php on line 154

なんてエラーが。
プラグインの更新だけでなくテーマの更新や、Wordpressそのものの更新(再インストール)もできなくなってます。

で、ちょっと調べたところ、どうやら、wp_tempnam 関数が無限ループになってるっぽい。
この問題の発生条件は「インストール先ディレクトリがルート(/)になっている場合」。

ルートにインストールなんて聞くと普通あり得ない状況に思えますが、Wordpressは、ftp経由での自分自身の更新ができるので、

  • 更新にはftpを使う
  • ftpの接続先はchrootされたアカウントになっており、ルート(/)がWordPressのディレクトリになっている

場合に、この状況が発生することになります。

とりえあえず、

--- wp-admin/includes/file.php.orig   2015-04-27 10:34:19.000000000 +0900
+++ wp-admin/includes/file.php    2015-04-27 12:21:59.000000000 +0900
@@ -146,7 +146,7 @@
                $dir = get_temp_dir();
        }
 
-       if ( empty( $filename ) || '.' == $filename ) {
+       if ( empty( $filename ) || '.' == $filename || '/' == $filename) {
                $filename = time();
        }

というパッチを当てて、問題なく更新できるようになってる感じ。

しかし、最近Wordpress がよく狙われてるという話を聞くので、マメに更新しておかないと…と思ったらコレだ。
こんなことなら、いつものように、新バージョンはしばらく様子見すべきだったか…

#「再帰呼び出し」は無限ループに入りかねない鬼門だよなぁ。
#実装するときは慎重にしないと…という他山の石。

この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。
  • 本家にて既にチケット発行済 [wordpress.org]のようです。4.2.2で直す予定か…
    日本語のフォーラムにパッチ付けてバグ報告したら、そのパッチがチケットに添付されてくれてた。

    php5.6で問題は起きてないとか的外れな指摘が書かれてたので、発生原因の詳細もコメントしておいた。

  • by Anonymous Coward on 2015年04月27日 13時53分 (#2804869)

    C 言語の tempnam() ってセキュリティ的にはあまりよろしくない,みたいな話がありませんでしたっけ.
    ファイル名を作った後にどこかの誰かがそのファイル名のファイルを作ることができるので,云々みたいな.

    PHP, というか Wordpress ではそのあたりうまいことやってるのかな.

    • ちょっと確認してみました。

      まず前提として、C言語標準ライブラリの tempnam は、呼び出し時点では存在しないファイル名を生成しますが、ファイル生成などは行わないので、
      「呼び出してから、その戻り値をつかってファイル生成するまで」の間に、ファイル名の衝突が起こる可能性があるので、使っちゃダメ。
      tempnamの代わりに mkstempやmkdtemp と使えってことになってますが、こっちの関数は、ファイル名生成と同時にファイル/ディレクトリを作成します。そのため、mkstemp同士が衝突する心配はありません。

      以上を踏まえた上で、

      ・PHP
      PHPには、C言語の標準ライブラリ関数と同仕様の、tempnam という組み込み関数がありますが、
      そのPHP関数のC言語実装(ext/standard/file.c)では、C言語のtempnam は使っていません。
      最終的に、main/php_open_temporary_file.c 内の php_do_open_temporary_file 関数が呼び出され、その中で、mkstemp を呼びだしてファイル名生成しています。

      つまり、PHP の tempnam は、その名前とはうらはらに、使っても全然問題ない関数ってことで。

      ・Wordpress
      wp-admin/includes/files.php 内に定義された関数 wp_tempnam は、
      wp-includes/functions.php 内に定義された関数 wp_unique_filename を呼びだしており、
      wp_unique_filename は、自前で一時ファイルの生成を行っています。

      その内容は…最終的に

      while ( file_exists( $dir . "/$filename" ) ) {
              if ( '' == "$number$ext" )
                      $filename = $filename . ++$number . $ext;
              else
                      $filename = str_replace( "$number$ext", ++$number . $ext, $filename );
      }

      というループ処理になってます。
      tempnamと一緒で、ファイル名生成とファイル作成処理がアトミックじゃないので、これではファイル名が衝突しない保証がありません。
      このままでも、利用する側でアトミックなファイルオープンをしていれば問題ありませんが、ざっと見たかぎりでは、利用側では何も考えずに生成ファイル名にファイル出力するようなコードばかりでした…
      まあ、利用者が一人だけ、というような個人ブログレベルなら、まず問題ないでしょうけど…

      親コメント
typodupeerror

ハッカーとクラッカーの違い。大してないと思います -- あるアレゲ

読み込み中...