[PATCH] Fix UTF-16 surrogate handling in __utf8_mbtowc

Jeff Johnston jjohnstn@redhat.com
Wed Jul 29 08:35:00 GMT 2009


Corinna Vinschen wrote:
> On Jul 28 14:56, Jeff Johnston wrote:
>   
>> Please go ahead.
>>     
>
> Thanks, will do.  But here's a question:
>
>   
>>> +	  tmp = (wint_t)((state->__value.__wchb[0] & 0x07) << 18)
>>> +	    |   (wint_t)((state->__value.__wchb[1] & 0x3f) << 12)
>>> +	    |   (wint_t)((state->__value.__wchb[2] & 0x3f) << 6);
>>> +	  tmp = (tmp - 0x10000) >> 10;
>>> +	  /* Check if the sequence can fit into a surrogate pair at all.
>>> +	     If tmp is > 0x3ff at this point, the full Unicode value will
>>> +	     be > 0x10ffff.  This is an invalid Unicode value and outside
>>> +	     of the defintion of UTF-16 surrogate pairs. */
>>> +	  if (tmp > 0x3ff)
>>> +	    {
>>> +	      r->_errno = EILSEQ;
>>> +	      return -1;
>>> +	    }
>>>       
>
> This code tests if the wide char value is within the boundaries of the
> Unicode specification, because it's impossible to represent bigger
> values (> 0x10ffff) as UTF-16 surrogate pair.
>
> However, the surrounding code still allows values > 0x10ffff for UTF-32
> systems.  So on these systems the code will happily allow and generate
> non-Unicode values.
>
> The question is, shouldn't the code be changed to disallow values beyond
> 0x10ffff on all systems, rather than just checking it in the UTF-16
> case?
>
>   
If the code allows those invalid sequences to generate and doesn't catch 
them at an earlier stage,
then it should be fixed, so go ahead, assuming you have tested the patch.

-- Jeff J.

> New patch attached, which checks for values beyond the Unicode range
> generically.  Whichever you prefer.
>
>
> Corinna
>
>
>         * libc/stdlib/mbtowc_r.c (__utf8_mbtowc): Rework UTF-16 surrogate
> 	pair handling to be more bullet-proof even with incomplete UTF-8
> 	sequences.  Add check for 4 byte sequences resulting in values
> 	outside the valid Unicode range.
>
>
> Index: libc/stdlib/mbtowc_r.c
> ===================================================================
> RCS file: /cvs/src/src/newlib/libc/stdlib/mbtowc_r.c,v
> retrieving revision 1.14
> diff -u -p -r1.14 mbtowc_r.c
> --- libc/stdlib/mbtowc_r.c	28 Jul 2009 16:49:19 -0000	1.14
> +++ libc/stdlib/mbtowc_r.c	28 Jul 2009 19:56:07 -0000
> @@ -205,18 +205,6 @@ _DEFUN (__utf8_mbtowc, (r, pwc, s, n, ch
>    if (n == 0)
>      return -2;
>  
> -  if (state->__count == 4)
> -    {
> -      /* Create the second half of the surrogate pair.  For a description
> -	 see the comment below. */
> -      wint_t tmp = (wchar_t)((state->__value.__wchb[0] & 0x07) << 18)
> -	|   (wchar_t)((state->__value.__wchb[1] & 0x3f) << 12)
> -	|   (wchar_t)((state->__value.__wchb[2] & 0x3f) << 6)
> -	|   (wchar_t)(state->__value.__wchb[3] & 0x3f);
> -      state->__count = 0;
> -      *pwc = 0xdc00 | ((tmp - 0x10000) & 0x3ff);
> -      return 2;
> -    }
>    if (state->__count == 0)
>      ch = t[i++];
>    else
> @@ -312,7 +300,7 @@ _DEFUN (__utf8_mbtowc, (r, pwc, s, n, ch
>        *pwc = tmp;
>        return i;
>      }
> -  if (ch >= 0xf0 && ch <= 0xf7)
> +  if (ch >= 0xf0 && ch <= 0xf4)
>      {
>        /* four-byte sequence */
>        wint_t tmp;
> @@ -324,9 +312,10 @@ _DEFUN (__utf8_mbtowc, (r, pwc, s, n, ch
>        if (n < 2)
>  	return -2;
>        ch = (state->__count == 1) ? t[i++] : state->__value.__wchb[1];
> -      if (state->__value.__wchb[0] == 0xf0 && ch < 0x90)
> +      if ((state->__value.__wchb[0] == 0xf0 && ch < 0x90)
> +	  || (state->__value.__wchb[0] == 0xf4 && ch >= 0x90))
>  	{
> -	  /* overlong UTF-8 sequence */
> +	  /* overlong UTF-8 sequence or result is > 0x10ffff */
>  	  r->_errno = EILSEQ;
>  	  return -1;
>  	}
> @@ -353,6 +342,26 @@ _DEFUN (__utf8_mbtowc, (r, pwc, s, n, ch
>  	state->__count = 3;
>        else if (n < (size_t)-1)
>  	++n;
> +      if (state->__count == 3 && sizeof(wchar_t) == 2)
> +	{
> +	  /* On systems which have wchar_t being UTF-16 values, the value
> +	     doesn't fit into a single wchar_t in this case.  So what we
> +	     do here is to store the state with a special value of __count
> +	     and return the first half of a surrogate pair.  The first
> +	     three bytes of a UTF-8 sequence are enough to generate the
> +	     first half of a UTF-16 surrogate pair.  As return value we
> +	     choose to return the number of bytes actually read up to
> +	     here.
> +	     The second half of the surrogate pair is returned in case we
> +	     recognize the special __count value of four, and the next
> +	     byte is actually a valid value.  See below. */
> +	  tmp = (wint_t)((state->__value.__wchb[0] & 0x07) << 18)
> +	    |   (wint_t)((state->__value.__wchb[1] & 0x3f) << 12)
> +	    |   (wint_t)((state->__value.__wchb[2] & 0x3f) << 6);
> +	  state->__count = 4;
> +	  *pwc = 0xd800 | ((tmp - 0x10000) >> 10);
> +	  return i;
> +	}
>        if (n < 4)
>  	return -2;
>        ch = t[i++];
> @@ -365,21 +374,12 @@ _DEFUN (__utf8_mbtowc, (r, pwc, s, n, ch
>  	|   (wint_t)((state->__value.__wchb[1] & 0x3f) << 12)
>  	|   (wint_t)((state->__value.__wchb[2] & 0x3f) << 6)
>  	|   (wint_t)(ch & 0x3f);
> -      if (tmp > 0xffff && sizeof(wchar_t) == 2)
> -	{
> -	  /* On systems which have wchar_t being UTF-16 values, the value
> -	     doesn't fit into a single wchar_t in this case.  So what we
> -	     do here is to store the state with a special value of __count
> -	     and return the first half of a surrogate pair.  As return
> -	     value we choose to return the half of the actual UTF-8 char.
> -	     The second half is returned in case we recognize the special
> -	     __count value above. */
> -	  state->__value.__wchb[3] = ch;
> -	  state->__count = 4;
> -	  *pwc = 0xd800 | (((tmp - 0x10000) >> 10) & 0x3ff);
> -	  return 2;
> -	}
> -      *pwc = tmp;
> +      if (state->__count == 4 && sizeof(wchar_t) == 2)
> +	/* Create the second half of the surrogate pair for systems with
> +	   wchar_t == UTF-16 . */
> +	*pwc = 0xdc00 | (tmp & 0x3ff);
> +      else
> +	*pwc = tmp;
>        state->__count = 0;
>        return i;
>      }
>
>
>   



More information about the Newlib mailing list