ます’s Blog - どうでもいい記事100選

どうでもいい記事100選

Refine fix for multibyte char hanling inside command names and args

この変更はマルチバイト圏のユーザーには厳しい予感がする()。
変更部分がphp_escape_shell_cmd関数内部とphp_escape_shell_arg関数内部なので、主にescapeshellcmd関数とescapeshellarg関数が影響を受けます。
どういう結果になるのか(一応)確認。

% cd /usr/local/src
% gzip -dc php-5.2.5.tar.gz | gtar xf -
% cd php-5.2.5
% ./configure \
--disable-all \
--without-iconv \
--enable-mbstring \
--enable-debug

% make
% cp sapi/cli/php /usr/local/src/php-5.2.5-cli

% cd /usr/local/src
% gzip -dc php5.2-200803250130.tar.gz | gtar xf -
% cd php5.2-200803250130
% ./configure \
--disable-all \
--without-iconv \
--enable-mbstring \
--enable-debug

% make
% cp sapi/cli/php /usr/local/src/php-5.2.6-dev-cli-200803250130

% cd /usr/local/src
% cat ./exec.php
<?php

  declare( encoding="EUC-JP" );

  var_dump( PHP_VERSION );

  var_dump( escapeshellcmd( "/usr/local/src/あ" ) );
  var_dump( escapeshellarg( "/usr/local/src/あ" ) );

  var_dump( escapeshellcmd( "'/usr/local/src/あ'" ) );
  var_dump( escapeshellarg( "'/usr/local/src/あ'" ) );

  var_dump( escapeshellcmd( '"/usr/local/src/あ"' ) );
  var_dump( escapeshellarg( '"/usr/local/src/あ"' ) );

  var_dump( escapeshellcmd( "/usr/local/あ/src" ) );
  var_dump( escapeshellarg( "/usr/local/あ/src" ) );

  var_dump( escapeshellcmd( "'/usr/local/あ/src'" ) );
  var_dump( escapeshellarg( "'/usr/local/あ/src'" ) );

  var_dump( escapeshellcmd( '"/usr/local/あ/src"' ) );
  var_dump( escapeshellarg( '"/usr/local/あ/src"' ) );

?>

% ./php-5.2.5-cli ./exec.php
string(5) "5.2.5"
string(17) "/usr/local/src/あ"
string(19) "'/usr/local/src/あ'"
string(19) "'/usr/local/src/あ'"
string(27) "''\''/usr/local/src/あ'\'''"
string(19) ""/usr/local/src/あ""
string(21) "'"/usr/local/src/あ"'"
string(17) "/usr/local/あ/src"
string(19) "'/usr/local/あ/src'"
string(19) "'/usr/local/あ/src'"
string(27) "''\''/usr/local/あ/src'\'''"
string(19) ""/usr/local/あ/src""
string(21) "'"/usr/local/あ/src"'"

% ./php-5.2.6-dev-cli-200803250130 ./exec.php
string(12) "5.2.6RC3-dev"
string(15) "/usr/local/src/"
string(17) "'/usr/local/src/'"
string(17) "'/usr/local/src/'"
string(25) "''\''/usr/local/src/'\'''"
string(17) ""/usr/local/src/""
string(19) "'"/usr/local/src/"'"
string(15) "/usr/local//src"
string(17) "'/usr/local//src'"
string(17) "'/usr/local//src'"
string(25) "''\''/usr/local//src'\'''"
string(17) ""/usr/local//src""
string(19) "'"/usr/local//src"'"

% uname -a
Linux 2.6.18-6-686 #1 SMP Sun Feb 10 22:11:31 UTC 2008 i686 GNU/Linux

ぎゃっ。
ただ、理由も無しに変更するハズは無いので何かしらの理由がありそう(不正なマルチバイトによるセキュリティ対策とか)。
変更点を注意深く見てみると、php_mblen関数の結果如何で挙動が変わってきそうな雰囲気。
もうだけ少し深追い。

% cd /usr/local/src/php5.2-200803250130
% grep -rn php_mblen .
./NEWS:2424:- Fixed bug #35243 (php_mblen() crashes when compiled with thread-safety on
./ext/standard/string.c:1366:           inc_len = (*c == '\0' ? 1: php_mblen(c, cnt));
./ext/standard/string.c:1372:                           php_mblen(NULL, 0);
./ext/standard/exec.c:276:              int mb_len = php_mblen(str + x, (l - x));
./ext/standard/exec.c:358:              int mb_len = php_mblen(str + x, (l - x));
./ext/standard/file.c:1884:             inc_len = (*ptr == '\0' ? 1: php_mblen(ptr, len));
./ext/standard/file.c:1889:                             php_mblen(NULL, 0);
./ext/standard/file.c:2119:     php_mblen(NULL, 0);
./ext/standard/file.c:2147:                     inc_len = (bptr < limit ? (*bptr == '\0' ? 1: php_mblen(bptr, limit - bptr)): 0);
./ext/standard/file.c:2152:                                     php_mblen(NULL, 0);
./ext/standard/file.c:2239:                                             php_mblen(NULL, 0);
./ext/standard/file.c:2294:                             inc_len = (bptr < limit ? (*bptr == '\0' ? 1: php_mblen(bptr, limit - bptr)): 0);
./ext/standard/file.c:2307:                                             php_mblen(NULL, 0);
./ext/standard/file.c:2318:                             inc_len = (bptr < limit ? (*bptr == '\0' ? 1: php_mblen(bptr, limit - bptr)): 0);
./ext/standard/file.c:2338:                                             php_mblen(NULL, 0);
./ext/standard/file.c:2349:                             inc_len = (bptr < limit ? (*bptr == '\0' ? 1: php_mblen(bptr, limit - bptr)): 0);
./ext/standard/php_string.h:150:# define php_mblen(ptr, len) 1
./ext/standard/php_string.h:153:#  define php_mblen(ptr, len) ((ptr) == NULL ? mbsinit(&BG(mblen_state)): (int)mbrlen(ptr, len, &BG(mblen_state)))
./ext/standard/php_string.h:155:#  define php_mblen(ptr, len) mblen(ptr, len)

% less -N ./ext/standard/php_string.h

        〜 省略 〜

    149 #ifndef HAVE_MBLEN
    150 # define php_mblen(ptr, len) 1
    151 #else
    152 # if defined(_REENTRANT) && defined(HAVE_MBRLEN) && defined(HAVE_MBSTATE_T)
    153 #  define php_mblen(ptr, len) ((ptr) == NULL ? mbsinit(&BG(mblen_state)): (int)mbrlen(ptr, len, &BG(mblen_state)))
    154 # else
    155 #  define php_mblen(ptr, len) mblen(ptr, len)
    156 # endif
    157 #endif

        〜 省略 〜

% grep -n HAVE_MBLEN ./main/php_config.h
358:#define HAVE_MBLEN 1

% grep -n HAVE_MBRLEN ./main/php_config.h
361:#define HAVE_MBRLEN 1

% grep -n HAVE_MBSTATE_T ./main/php_config.h
2653:#define HAVE_MBSTATE_T 1

% grep -rn _REENTRANT .
./configure:12662:    PTHREAD_FLAGS="-D_POSIX_PTHREAD_SEMANTICS -D_REENTRANT";;
./configure:12664:    PTHREAD_FLAGS="-D_REENTRANT -D_THREAD_SAFE";;
./configure:12666:    PTHREAD_FLAGS=-D_REENTRANT;;
./configure:12672:    PTHREAD_FLAGS=-D_REENTRANT;;
./configure:12674:    PTHREAD_FLAGS=-D_REENTRANT;;
./configure:18716:#define _REENTRANT
./configure:18752:#define _REENTRANT
./configure:18906:#define _REENTRANT 1
./configure:18930:#define _REENTRANT 1
./configure:18955:#define _REENTRANT 1
./aclocal.m4:1337:#define _REENTRANT
./aclocal.m4:1361:#define _REENTRANT
./aclocal.m4:2770:#define _REENTRANT 1
./aclocal.m4:2780:#define _REENTRANT 1
./aclocal.m4:2791:#define _REENTRANT 1
./ext/standard/php_string.h:152:# if defined(_REENTRANT) && defined(HAVE_MBRLEN) && defined(HAVE_MBSTATE_T)
./ext/standard/basic_functions.h:215:#if defined(_REENTRANT) && defined(HAVE_MBRLEN) && defined(HAVE_MBSTATE_T)
./ext/standard/basic_functions.c:3916:#if defined(_REENTRANT) && defined(HAVE_MBRLEN) && defined(HAVE_MBSTATE_T)
./ext/standard/crypt.c:151:#if defined(HAVE_CRYPT_R) && (defined(_REENTRANT) || defined(_THREAD_SAFE))
./acinclude.m4:1337:#define _REENTRANT
./acinclude.m4:1361:#define _REENTRANT
./acinclude.m4:2770:#define _REENTRANT 1
./acinclude.m4:2780:#define _REENTRANT 1
./acinclude.m4:2791:#define _REENTRANT 1
./sapi/roxen/roxen.c:114:#if defined(_REENTRANT) && !defined(ROXEN_USE_ZTS)
./sapi/roxen/roxen.c:123:#else /* !_REENTRANT */
./sapi/roxen/roxen.c:128:#endif /* _REENTRANT */
./TSRM/threads.m4:42:    PTHREAD_FLAGS="-D_POSIX_PTHREAD_SEMANTICS -D_REENTRANT";;
./TSRM/threads.m4:44:    PTHREAD_FLAGS="-D_REENTRANT -D_THREAD_SAFE";;
./TSRM/threads.m4:46:    PTHREAD_FLAGS=-D_REENTRANT;;
./TSRM/threads.m4:52:    PTHREAD_FLAGS=-D_REENTRANT;;
./TSRM/threads.m4:54:    PTHREAD_FLAGS=-D_REENTRANT;;
./TSRM/threads.m4:56:dnl    PTHREAD_FLAGS="-D_REENTRANT -D_XOPEN_SOURCE=500 -D_POSIX_C_SOURCE=199506 -D_XOPEN_SOURCE_EXTENDED=1";;

155行目が有効になっている。。。と。で、mblen関数はlocaleに依存する。。。と。_| ̄|○
もし、mblen関数が腐った実装だったら。。。ひぇ。
それにしても、今後はlocaleに注意しながら実装しないといけないの?うーん。