[PATCH] Bug in _mbtowc_r UTF-8 handling
Jeff Johnston
jjohnstn@redhat.com
Tue Jul 28 16:57:00 GMT 2009
Corinna Vinschen wrote:
> Hi,
>
> there's a bug in __utf8_mbtowc, which appears to be in that code for
> ages. If you give an UTF-8 multibyte character to this function one
> at a time, it screws up counting and testing the value `n', the number
> of bytes in the input stream.
>
> Here's a testcase:
>
> #include <stdio.h>
> #include <locale.h>
> #include <stdlib.h>
> #include <wchar.h>
>
> int main(void) {
> wchar_t wc;
> size_t ret;
> mbstate_t s = { 0 };
> puts(setlocale(LC_CTYPE, "en_GB.UTF-8"));
> printf("%i\n", mbrtowc(&wc, "\xe2", 1, &s));
> printf("%i\n", mbrtowc(&wc, "\x94", 1, &s));
> printf("%i\n", mbrtowc(&wc, "\x84", 1, &s));
> printf("%x\n", wc);
> return 0;
> }
>
> That's the expected output:
>
> en_GB.UTF-8
> -2
> -2
> 1
> 2504
>
> That's what happens with newlib's implementation:
>
> en_GB.UTF-8
> -2
> -1
> -1
> <arbitrary value>
>
> The sequence E2 94 84 should translate to U+2504. Instead, the second
> and third calls to mbrtowc report encoding errors. It does work
> correctly if the three bytes are passed to mbrtowc() in one go:
>
> printf("%i\n", mbrtowc(&wc, "\xe2\x94\x84", 3, 0));
>
> As I mentioned afore, the problem is how the argument `n' is counted
> when `state' is already filled and subsequent calls encounter a
> non-empty state. As soon as the incoming byte sequence consists of
> a multibyte followup sequence, and this sequence is in itself incomplete
> again, the value of `n' is wrong and the tests which are supposed to
> return -2 fail. The code slips through, reading a char too much, and
> either fails, or, worst case, returns an incorrect wchar_t value.
> Given that, the problem never happens for two-byte sequences, only
> for three- and four-byte sequences.
>
> I created a patch to fix this. The difference to the original code is a
> more unified way to increment `n'. The original code incremented `n' by
> one too much in the above scenario. Tested on Cygwin with 2, 3, and 4
> byte sequences.
>
> This way I found another problem in the UTF-16 surrogate handling which
> only occurs with incomplete 4-byte sequences. I have no fix for this
> and I'm not sure yet if there's a satisfying way to fix this at all.
>
> So the below patch only fixes the above problem, not the UTF-16
> related problem.
>
> Ok to apply?
>
>
>
Please go ahead.
-- Jeff J.
> Thanks,
> Corinna
>
>
> * libc/stdlib/mbtowc_r.c (__utf8_mbtowc): Fix incrementing n
> in case of handling incomplete sequences.
>
>
> Index: libc/stdlib/mbtowc_r.c
> ===================================================================
> RCS file: /cvs/src/src/newlib/libc/stdlib/mbtowc_r.c,v
> retrieving revision 1.13
> diff -u -p -r1.13 mbtowc_r.c
> --- libc/stdlib/mbtowc_r.c 9 Apr 2009 08:20:10 -0000 1.13
> +++ libc/stdlib/mbtowc_r.c 28 Jul 2009 10:01:20 -0000
> @@ -220,11 +220,7 @@ _DEFUN (__utf8_mbtowc, (r, pwc, s, n, ch
> if (state->__count == 0)
> ch = t[i++];
> else
> - {
> - if (n < (size_t)-1)
> - ++n;
> - ch = state->__value.__wchb[0];
> - }
> + ch = state->__value.__wchb[0];
>
> if (ch == '\0')
> {
> @@ -244,7 +240,10 @@ _DEFUN (__utf8_mbtowc, (r, pwc, s, n, ch
> {
> /* two-byte sequence */
> state->__value.__wchb[0] = ch;
> - state->__count = 1;
> + if (state->__count == 0)
> + state->__count = 1;
> + else if (n < (size_t)-1)
> + ++n;
> if (n < 2)
> return -2;
> ch = t[i++];
> @@ -288,7 +287,10 @@ _DEFUN (__utf8_mbtowc, (r, pwc, s, n, ch
> return -1;
> }
> state->__value.__wchb[1] = ch;
> - state->__count = 2;
> + if (state->__count == 1)
> + state->__count = 2;
> + else if (n < (size_t)-1)
> + ++n;
> if (n < 3)
> return -2;
> ch = t[i++];
> @@ -347,7 +349,10 @@ _DEFUN (__utf8_mbtowc, (r, pwc, s, n, ch
> return -1;
> }
> state->__value.__wchb[2] = ch;
> - state->__count = 3;
> + if (state->__count == 2)
> + state->__count = 3;
> + else if (n < (size_t)-1)
> + ++n;
> if (n < 4)
> return -2;
> ch = t[i++];
>
>
>
More information about the Newlib
mailing list