[PHP-dev 1397] mb_check_encoding() の第1引数の最初に \0 が入力された場合の問題について
komura
komura.db2r1e @ gmail.com
2008年 5月 18日 (日) 19:01:53 JST
komura です。
1つ前のメールにも関連することなのですが、mb_check_encoding() の第1引数
の最初の文字列に \0 を入力すると、条件によっては、必ず TRUE になる問題
があります。
例としては、以下の通りです。
1. mbstring.substitute_character に "none" が指定された場合
<?php
mb_substitute_character( 'none' );
$result = mb_check_encoding( "\0\xFF\xFF\xFF", 'SJIS' );
var_dump( $result );
?>
-- 結果 --
bool(true)
2. 第2引数のエンコーディングに UTF-8 が指定された場合
<?php
mb_substitute_character( 63 );
$result = mb_check_encoding( "\0\xFF\xFF\xFF", 'UTF-8' );
var_dump( $result );
?>
-- 結果 --
bool(true)
以上の問題が発生するのは、mb_check_encoding() の処理が以下のように
なっているためです。
1) 第2引数で指定されたエンコーディングで第1引数の文字列を変換
2) 不正文字カウント(illegal_chars) が 0 で、さらに、変換前と
変換後の文字列が同一の場合、TRUE を返す(比較関数として
strncmp() を使用)
不正文字カウント(illegal_chars) が正しくカウントされない理由は
以下の通りです。
1. substitute_character を "none" に設定した場合、illegal_chars の
カウントが行われない(ext/mbstring/libmbfl/filters/mbfilter_*.c)
各ファイルの mbfl_filt_conv_wchar_*() を見ると、
filter->illegal_mode が MBFL_OUTPUTFILTER_ILLEGAL_MODE_NONE
(ubstitute_character を "none" にすると設定される) の場合は
illegal_chars のカウントが行われないコードになっています。
2. UTF-8 は不正文字の判定が厳しくないため、ほとんどの場合、
illegal_chars のカウントが行われない
(ext/mbstring/libmbfl/filters/mbfilter_utf8.c)
illegal_chars がカウントされる可能性が非常に低いコードになって
います。
2) に関してですが、strncmp() は '\0' 以降の比較を行わないという
ことが原因です。不正文字カウントが 0 でも、変換後の文字列は破壊
されていますので、バイナリ比較を行えば、正しく判定できるように
思います。
この部分(strncmp() を使用するようにした)は以前私が Patch を書き
ましたが、本件の問題については考慮不足でした。すみません。
substitute_character の挙動を変更するのは非常に大変ですので、
mb_check_encoding() の変換後の文字列比較を行っている部分を
バイナリで行うようにする Patch を作成してみました。
diff -ru php-5.2.6.orig/ext/mbstring/mbstring.c php-5.2.6/ext/mbstring/mbstring.c
--- php-5.2.6.orig/ext/mbstring/mbstring.c 2008-02-17 11:06:56.000000000 +0900
+++ php-5.2.6/ext/mbstring/mbstring.c 2008-05-18 18:13:51.007778173 +0900
@@ -4088,7 +4088,7 @@
if (ret != NULL) {
MBSTRG(illegalchars) += illegalchars;
- if (illegalchars == 0 && strncmp(string.val, ret->val, string.len) == 0) {
+ if (illegalchars == 0 && memcmp(string.val, ret->val, string.len) == 0) {
efree(ret->val);
RETURN_TRUE;
} else {
--
komura <komura.db2r1e @ gmail.com>
PHP-dev メーリングリストの案内