You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
mb_detect_encoding() results for UTF-7 differ between PHP 8.0 and 8.1 (if UTF-7 is present in the encodings list and the string contains '+' character) #10192
changed the title [-]PHP 8.2 and PHP 8.1 Bug serious bug with strings encoding detection[/-][+]PHP 8.2 and PHP 8.1 Bug serious bug with strings (encoding not UTF-8 if + character is present in any string)[/+]on Dec 30, 2022
changed the title [-]PHP 8.2 and PHP 8.1 Bug serious bug with strings (encoding not UTF-8 if + character is present in any string)[/-][+]PHP 8.2 and PHP 8.1 Bug serious bug with strings (encoding not UTF-8 if a `+` character is present in a string)[/+]on Dec 30, 2022
changed the title [-]PHP 8.2 and PHP 8.1 Bug serious bug with strings (encoding not UTF-8 if a `+` character is present in a string)[/-][+]PHP 8.(1|2) mb_detect_encoding() regression when string contains '+' is determined as UTF-7 instead of UTF-8[/+]on Dec 30, 2022
I honestly doubt the severity of this bug as AFAIK any UTF-7 string is valid UTF-8, moreover mb_detect_encoding() is a misnomer as it tries to guess what encoding a string might be.
But I'll let @alexdowad comment, as he's the character encoding expect and has been refactoring mbstring.
It is very serious. Because if you need to normalize the output you rely on re-encoding all strings as UTF-8.
Thus, if re-encode 'A + B' from UTF-7 (which is actually UTF-8 already, but is wrong detected by PHP 8.1/8.2) to UTF-8 results in 'A B' (the plus sign disappears), as the strings get corrupted.
Actually this is how I start investigating and get to this bug.
Scenario:
Read from a data source -> Filter UTF-8 or re-encode as UTF-8 -> Output. If a string contains '+' sign will be mangled after normalization (re-encode as UTF-8 which actually would be not necessary if UTF-8 would be correctly detected as in PHP 8.0 / 7.4).
wrong output in PHP 8.1 / 8.2 (the + character is mangled, disappeared)
UTF-7
A B
UTF-8
A - B
Thus in my opinion would affect a lot of database data. This is the most serious bug in PHP since I have seen. If you re-save in database all the data becomes corrupted without a chance to go backward only if you have a valid backup !!
I suspect a memory corruption there or something very ugly ...
If I convert a string from UTF-7 to UTF-8 should be preserved as is. Right ?
But actually because a string which contains + character is already UTF-8 but PHP detects it as UTF-7 it corrupts that string on re-encoding and + characters vanish ... !
EDIT: Sorry, you can ignore this comment. In UTF7-IMAP the closing delimiter is specified as -. So, it is different. I'll shut up now.
Just to provide some historical background, when UTF7-IMAP was designed, an explicit design goal was that there should be one, and only one, possible way to encode any given string. It was deliberately made stricter than UTF-7.
@Tofandel, mb_detect_encoding() is not a magic encoding/charset detector. Actually, there is no such thing, at least if the strings to check are short. If you are expecting UTF-8, I suggest to reject anything else, and not magically try to convert it.
@Tofandel, thanks for letting us know about the issue you are experiencing.
It's not really the main point, but anyways, a + character (U+002B PLUS SIGN) is not represented by the single UTF-7 byte '+'. In UTF-7, a plus sign is represented by the two bytes '+-'. So it's not really correct to say that mb_convert_encoding is "dropping" plus signs when converting UTF-7 strings; those 0x2B bytes don't represent plus signs in UTF-7.
A bare '+' like you showed in the code above is invalid according to the UTF-7 RFC, so mbstring should treat it as an error; as you can see from the above discussion, I am just trying to figure out when this change should be made.
@cmb69's recommendation is very good, and you would probably be wise to follow it (though we don't know all the details of your application). However, even if you have good reason to use mb_detect_encoding, I would really suggest that mb_detect_encoding(..., mb_list_encodings()) is asking for trouble. Rather than mb_list_encodings(), it is much better to provide an explicit list of the text encodings which you actually need to support. For many applications, it might be something like ['UTF-8', 'UTF-16LE', 'UTF-16BE'], perhaps with a couple of legacy text encodings added to the list.
Supporting UTF-7 is quite dubious. The name "UTF-7" might make you think this is a "modern" way of encoding text, but that is not true at all. Wikipedia describes UTF-7 as "obsolete", and that is 100% true. It's a weird, badly designed text encoding which is hardly ever used for anything.
About the mb_list_encodings thing, when I finish the work I am currently doing on the mbstring codebase and start working on the documentation, I intend to put a warning right in the PHP manual not to pass mb_list_encodings() to mb_detect_encoding. It's just a bad idea.
Even so, I do acknowledge that many PHP users will continue to use mb_detect_encoding like this. When we make the UTF-7 conversion code a bit stricter about illegal uses of '+', it will help to prevent UTF-8 text from being accidentally identified as UTF-7. Again, we just need to figure out when to make this change and how to go about it.
Just working on a patch to be slightly more strict when checking whether an input string might be UTF-7. I am adding @Tofandel's test case to the official test suite. This should be incorporated in the upcoming release of PHP 8.3.
@Lehren Thanks for the report. Frankly, I think the behavior in PHP 8.1+ makes more sense. Why would we expect to find U+031E COMBINING DOWN TACK BELOW coming after an ASCII space character?
Still, that is a valid (though nonsensical) UTF-8 string, so if all you really care about is that the string is valid UTF-8, then you can use mb_check_encoding($str, 'UTF-8'). That is a much better idea even for PHP 8.0 and below; for one thing, it's much faster.
This was fixed in #11239, but this issue was not closed automatically (despite being linked to the PR). So I'm closing this manually. If this is a mistake, let me know and I'll reopen.
Activity
[-]PHP 8.2 and PHP 8.1 Bug[/-][+]PHP 8.2 and PHP 8.1 Bug serious bug with strings encoding detection[/+][-]PHP 8.2 and PHP 8.1 Bug serious bug with strings encoding detection[/-][+]PHP 8.2 and PHP 8.1 Bug serious bug with strings (encoding not UTF-8 if + character is present in any string)[/+][-]PHP 8.2 and PHP 8.1 Bug serious bug with strings (encoding not UTF-8 if + character is present in any string)[/-][+]PHP 8.2 and PHP 8.1 Bug serious bug with strings (encoding not UTF-8 if a `+` character is present in a string)[/+][-]PHP 8.2 and PHP 8.1 Bug serious bug with strings (encoding not UTF-8 if a `+` character is present in a string)[/-][+]PHP 8.(1|2) mb_detect_encoding() regression when string contains '+' is determined as UTF-7 instead of UTF-8[/+]Girgias commentedon Dec 30, 2022
I honestly doubt the severity of this bug as AFAIK any UTF-7 string is valid UTF-8, moreover
mb_detect_encoding()is a misnomer as it tries to guess what encoding a string might be.But I'll let @alexdowad comment, as he's the character encoding expect and has been refactoring mbstring.
unix-world commentedon Dec 30, 2022
It is very serious. Because if you need to normalize the output you rely on re-encoding all strings as UTF-8.
Thus, if re-encode 'A + B' from UTF-7 (which is actually UTF-8 already, but is wrong detected by PHP 8.1/8.2) to UTF-8 results in 'A B' (the plus sign disappears), as the strings get corrupted.
Actually this is how I start investigating and get to this bug.
Scenario:
Read from a data source -> Filter UTF-8 or re-encode as UTF-8 -> Output. If a string contains '+' sign will be mangled after normalization (re-encode as UTF-8 which actually would be not necessary if UTF-8 would be correctly detected as in PHP 8.0 / 7.4).
unix-world commentedon Dec 30, 2022
It impacts so many things in an unpredictable way so I can't explain all situations here.
unix-world commentedon Dec 30, 2022
I updated the example for you to see the real disaster !
expected output (OK in PHP 8.0 / 7.4)
wrong output in PHP 8.1 / 8.2 (the + character is mangled, disappeared)
Thus in my opinion would affect a lot of database data. This is the most serious bug in PHP since I have seen. If you re-save in database all the data becomes corrupted without a chance to go backward only if you have a valid backup !!
unix-world commentedon Dec 30, 2022
I suspect a memory corruption there or something very ugly ...
If I convert a string from UTF-7 to UTF-8 should be preserved as is. Right ?
But actually because a string which contains
+character is already UTF-8 but PHP detects it as UTF-7 it corrupts that string on re-encoding and+characters vanish ... !unix-world commentedon Dec 30, 2022
And if 'A + B' is detected as UTF-7 instead of UTF-8 on PHP 8.1/8.2,... why 'A - B' is detected as UTF-8 ? Can't you see the anomaly in this case ?
36 remaining items
alexdowad commentedon Jan 5, 2023
Just to provide some historical background, when UTF7-IMAP was designed, an explicit design goal was that there should be one, and only one, possible way to encode any given string. It was deliberately made stricter than UTF-7.
Tofandel commentedon Jan 18, 2023
It's in fact a serious issue that affects us too, because "+" characters are being dropped when doing mb_convert_encoding("+", "UTF-8", "UTF-7");
PHP8.1+
PHP8.0
cmb69 commentedon Jan 18, 2023
@Tofandel,
mb_detect_encoding()is not a magic encoding/charset detector. Actually, there is no such thing, at least if the strings to check are short. If you are expecting UTF-8, I suggest to reject anything else, and not magically try to convert it.Anyhow, I leave this for @alexdowad to decide.
alexdowad commentedon Jan 18, 2023
@Tofandel, thanks for letting us know about the issue you are experiencing.
It's not really the main point, but anyways, a + character (U+002B PLUS SIGN) is not represented by the single UTF-7 byte '+'. In UTF-7, a plus sign is represented by the two bytes '+-'. So it's not really correct to say that
mb_convert_encodingis "dropping" plus signs when converting UTF-7 strings; those 0x2B bytes don't represent plus signs in UTF-7.A bare '+' like you showed in the code above is invalid according to the UTF-7 RFC, so mbstring should treat it as an error; as you can see from the above discussion, I am just trying to figure out when this change should be made.
@cmb69's recommendation is very good, and you would probably be wise to follow it (though we don't know all the details of your application). However, even if you have good reason to use
mb_detect_encoding, I would really suggest thatmb_detect_encoding(..., mb_list_encodings())is asking for trouble. Rather thanmb_list_encodings(), it is much better to provide an explicit list of the text encodings which you actually need to support. For many applications, it might be something like['UTF-8', 'UTF-16LE', 'UTF-16BE'], perhaps with a couple of legacy text encodings added to the list.Supporting UTF-7 is quite dubious. The name "UTF-7" might make you think this is a "modern" way of encoding text, but that is not true at all. Wikipedia describes UTF-7 as "obsolete", and that is 100% true. It's a weird, badly designed text encoding which is hardly ever used for anything.
About the
mb_list_encodingsthing, when I finish the work I am currently doing on the mbstring codebase and start working on the documentation, I intend to put a warning right in the PHP manual not to passmb_list_encodings()tomb_detect_encoding. It's just a bad idea.Even so, I do acknowledge that many PHP users will continue to use
mb_detect_encodinglike this. When we make the UTF-7 conversion code a bit stricter about illegal uses of '+', it will help to prevent UTF-8 text from being accidentally identified as UTF-7. Again, we just need to figure out when to make this change and how to go about it.Lehren commentedon Mar 22, 2023
It's not just for UTF-7. Since PHP 8.1.7 and up thinks this string is Windows-1252, even though it's seen as UTF-8 in PHP 8.0, 7.4, and 8.1.2
' ̞>'It contains the U+031E character
mb_detect_encoding(' ̞>',['ASCII','UTF-8', 'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-6','ISO-8859-15', 'Windows-1252'])Another example is
mb_detect_encoding('Ⱥ',['UTF-8', 'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-6','ISO-8859-15', 'Windows-1252','ASCII'])in PHP 8.1 and up it's seen as ISO-8859-1, and in PHP 8.0 as UTF-8
alexdowad commentedon May 14, 2023
Just working on a patch to be slightly more strict when checking whether an input string might be UTF-7. I am adding @Tofandel's test case to the official test suite. This should be incorporated in the upcoming release of PHP 8.3.
alexdowad commentedon May 14, 2023
@Lehren Thanks for the report. Frankly, I think the behavior in PHP 8.1+ makes more sense. Why would we expect to find U+031E COMBINING DOWN TACK BELOW coming after an ASCII space character?
Still, that is a valid (though nonsensical) UTF-8 string, so if all you really care about is that the string is valid UTF-8, then you can use
mb_check_encoding($str, 'UTF-8'). That is a much better idea even for PHP 8.0 and below; for one thing, it's much faster.ndossche commentedon Nov 11, 2023
This was fixed in #11239, but this issue was not closed automatically (despite being linked to the PR). So I'm closing this manually. If this is a mistake, let me know and I'll reopen.