Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date: Mon, 12 Jun 2023 17:01:59 -0400
From: Rich Felker <dalias@...c.org>
To: Bruno Haible <bruno@...sp.org>
Cc: musl@...ts.openwall.com
Subject: Re: swprintf cannot handle the character 0xff

On Mon, Jun 12, 2023 at 04:22:42PM -0400, Rich Felker wrote:
> On Mon, Jun 12, 2023 at 02:30:44PM +0200, Bruno Haible wrote:
> > When swprintf is meant to convert a character to a wide character, through
> > the %c directive, it fails if that character is '\xff'.
> > 
> > Seen with musl libc 1.2.4, in Alpine Linux 3.18.0.
> > 
> > How to reproduce:
> > ============================ foo.c ============================
> > #include <stdio.h>
> > #include <wchar.h>
> > int main ()
> > {
> >   wchar_t buf[12];
> >   for (int c = 1; c < 256; c++)
> >     {
> >       fprintf (stderr, "c = %d: ", c);
> >       int ret = swprintf (buf, 12, L"%c", c);
> >       if (ret >= 0)
> >         fprintf (stderr, "OK, %d bytes\n", ret);
> >       else
> >         perror ("swprintf failed");
> >     }
> > }
> > ===============================================================
> > $ gcc -Wall foo.c
> > $ ./a.out
> > 
> > Expected output:
> > c = 1: OK, 1 bytes
> > c = 2: OK, 1 bytes
> > c = 3: OK, 1 bytes
> > c = 4: OK, 1 bytes
> > c = 5: OK, 1 bytes
> > c = 6: OK, 1 bytes
> > c = 7: OK, 1 bytes
> > c = 8: OK, 1 bytes
> > c = 9: OK, 1 bytes
> > c = 10: OK, 1 bytes
> > c = 11: OK, 1 bytes
> > c = 12: OK, 1 bytes
> > c = 13: OK, 1 bytes
> > c = 14: OK, 1 bytes
> > c = 15: OK, 1 bytes
> > c = 16: OK, 1 bytes
> > c = 17: OK, 1 bytes
> > c = 18: OK, 1 bytes
> > c = 19: OK, 1 bytes
> > c = 20: OK, 1 bytes
> > c = 21: OK, 1 bytes
> > c = 22: OK, 1 bytes
> > c = 23: OK, 1 bytes
> > c = 24: OK, 1 bytes
> > c = 25: OK, 1 bytes
> > c = 26: OK, 1 bytes
> > c = 27: OK, 1 bytes
> > c = 28: OK, 1 bytes
> > c = 29: OK, 1 bytes
> > c = 30: OK, 1 bytes
> > c = 31: OK, 1 bytes
> > c = 32: OK, 1 bytes
> > c = 33: OK, 1 bytes
> > c = 34: OK, 1 bytes
> > c = 35: OK, 1 bytes
> > c = 36: OK, 1 bytes
> > c = 37: OK, 1 bytes
> > c = 38: OK, 1 bytes
> > c = 39: OK, 1 bytes
> > c = 40: OK, 1 bytes
> > c = 41: OK, 1 bytes
> > c = 42: OK, 1 bytes
> > c = 43: OK, 1 bytes
> > c = 44: OK, 1 bytes
> > c = 45: OK, 1 bytes
> > c = 46: OK, 1 bytes
> > c = 47: OK, 1 bytes
> > c = 48: OK, 1 bytes
> > c = 49: OK, 1 bytes
> > c = 50: OK, 1 bytes
> > c = 51: OK, 1 bytes
> > c = 52: OK, 1 bytes
> > c = 53: OK, 1 bytes
> > c = 54: OK, 1 bytes
> > c = 55: OK, 1 bytes
> > c = 56: OK, 1 bytes
> > c = 57: OK, 1 bytes
> > c = 58: OK, 1 bytes
> > c = 59: OK, 1 bytes
> > c = 60: OK, 1 bytes
> > c = 61: OK, 1 bytes
> > c = 62: OK, 1 bytes
> > c = 63: OK, 1 bytes
> > c = 64: OK, 1 bytes
> > c = 65: OK, 1 bytes
> > c = 66: OK, 1 bytes
> > c = 67: OK, 1 bytes
> > c = 68: OK, 1 bytes
> > c = 69: OK, 1 bytes
> > c = 70: OK, 1 bytes
> > c = 71: OK, 1 bytes
> > c = 72: OK, 1 bytes
> > c = 73: OK, 1 bytes
> > c = 74: OK, 1 bytes
> > c = 75: OK, 1 bytes
> > c = 76: OK, 1 bytes
> > c = 77: OK, 1 bytes
> > c = 78: OK, 1 bytes
> > c = 79: OK, 1 bytes
> > c = 80: OK, 1 bytes
> > c = 81: OK, 1 bytes
> > c = 82: OK, 1 bytes
> > c = 83: OK, 1 bytes
> > c = 84: OK, 1 bytes
> > c = 85: OK, 1 bytes
> > c = 86: OK, 1 bytes
> > c = 87: OK, 1 bytes
> > c = 88: OK, 1 bytes
> > c = 89: OK, 1 bytes
> > c = 90: OK, 1 bytes
> > c = 91: OK, 1 bytes
> > c = 92: OK, 1 bytes
> > c = 93: OK, 1 bytes
> > c = 94: OK, 1 bytes
> > c = 95: OK, 1 bytes
> > c = 96: OK, 1 bytes
> > c = 97: OK, 1 bytes
> > c = 98: OK, 1 bytes
> > c = 99: OK, 1 bytes
> > c = 100: OK, 1 bytes
> > c = 101: OK, 1 bytes
> > c = 102: OK, 1 bytes
> > c = 103: OK, 1 bytes
> > c = 104: OK, 1 bytes
> > c = 105: OK, 1 bytes
> > c = 106: OK, 1 bytes
> > c = 107: OK, 1 bytes
> > c = 108: OK, 1 bytes
> > c = 109: OK, 1 bytes
> > c = 110: OK, 1 bytes
> > c = 111: OK, 1 bytes
> > c = 112: OK, 1 bytes
> > c = 113: OK, 1 bytes
> > c = 114: OK, 1 bytes
> > c = 115: OK, 1 bytes
> > c = 116: OK, 1 bytes
> > c = 117: OK, 1 bytes
> > c = 118: OK, 1 bytes
> > c = 119: OK, 1 bytes
> > c = 120: OK, 1 bytes
> > c = 121: OK, 1 bytes
> > c = 122: OK, 1 bytes
> > c = 123: OK, 1 bytes
> > c = 124: OK, 1 bytes
> > c = 125: OK, 1 bytes
> > c = 126: OK, 1 bytes
> > c = 127: OK, 1 bytes
> > c = 128: OK, 1 bytes
> > c = 129: OK, 1 bytes
> > c = 130: OK, 1 bytes
> > c = 131: OK, 1 bytes
> > c = 132: OK, 1 bytes
> > c = 133: OK, 1 bytes
> > c = 134: OK, 1 bytes
> > c = 135: OK, 1 bytes
> > c = 136: OK, 1 bytes
> > c = 137: OK, 1 bytes
> > c = 138: OK, 1 bytes
> > c = 139: OK, 1 bytes
> > c = 140: OK, 1 bytes
> > c = 141: OK, 1 bytes
> > c = 142: OK, 1 bytes
> > c = 143: OK, 1 bytes
> > c = 144: OK, 1 bytes
> > c = 145: OK, 1 bytes
> > c = 146: OK, 1 bytes
> > c = 147: OK, 1 bytes
> > c = 148: OK, 1 bytes
> > c = 149: OK, 1 bytes
> > c = 150: OK, 1 bytes
> > c = 151: OK, 1 bytes
> > c = 152: OK, 1 bytes
> > c = 153: OK, 1 bytes
> > c = 154: OK, 1 bytes
> > c = 155: OK, 1 bytes
> > c = 156: OK, 1 bytes
> > c = 157: OK, 1 bytes
> > c = 158: OK, 1 bytes
> > c = 159: OK, 1 bytes
> > c = 160: OK, 1 bytes
> > c = 161: OK, 1 bytes
> > c = 162: OK, 1 bytes
> > c = 163: OK, 1 bytes
> > c = 164: OK, 1 bytes
> > c = 165: OK, 1 bytes
> > c = 166: OK, 1 bytes
> > c = 167: OK, 1 bytes
> > c = 168: OK, 1 bytes
> > c = 169: OK, 1 bytes
> > c = 170: OK, 1 bytes
> > c = 171: OK, 1 bytes
> > c = 172: OK, 1 bytes
> > c = 173: OK, 1 bytes
> > c = 174: OK, 1 bytes
> > c = 175: OK, 1 bytes
> > c = 176: OK, 1 bytes
> > c = 177: OK, 1 bytes
> > c = 178: OK, 1 bytes
> > c = 179: OK, 1 bytes
> > c = 180: OK, 1 bytes
> > c = 181: OK, 1 bytes
> > c = 182: OK, 1 bytes
> > c = 183: OK, 1 bytes
> > c = 184: OK, 1 bytes
> > c = 185: OK, 1 bytes
> > c = 186: OK, 1 bytes
> > c = 187: OK, 1 bytes
> > c = 188: OK, 1 bytes
> > c = 189: OK, 1 bytes
> > c = 190: OK, 1 bytes
> > c = 191: OK, 1 bytes
> > c = 192: OK, 1 bytes
> > c = 193: OK, 1 bytes
> > c = 194: OK, 1 bytes
> > c = 195: OK, 1 bytes
> > c = 196: OK, 1 bytes
> > c = 197: OK, 1 bytes
> > c = 198: OK, 1 bytes
> > c = 199: OK, 1 bytes
> > c = 200: OK, 1 bytes
> > c = 201: OK, 1 bytes
> > c = 202: OK, 1 bytes
> > c = 203: OK, 1 bytes
> > c = 204: OK, 1 bytes
> > c = 205: OK, 1 bytes
> > c = 206: OK, 1 bytes
> > c = 207: OK, 1 bytes
> > c = 208: OK, 1 bytes
> > c = 209: OK, 1 bytes
> > c = 210: OK, 1 bytes
> > c = 211: OK, 1 bytes
> > c = 212: OK, 1 bytes
> > c = 213: OK, 1 bytes
> > c = 214: OK, 1 bytes
> > c = 215: OK, 1 bytes
> > c = 216: OK, 1 bytes
> > c = 217: OK, 1 bytes
> > c = 218: OK, 1 bytes
> > c = 219: OK, 1 bytes
> > c = 220: OK, 1 bytes
> > c = 221: OK, 1 bytes
> > c = 222: OK, 1 bytes
> > c = 223: OK, 1 bytes
> > c = 224: OK, 1 bytes
> > c = 225: OK, 1 bytes
> > c = 226: OK, 1 bytes
> > c = 227: OK, 1 bytes
> > c = 228: OK, 1 bytes
> > c = 229: OK, 1 bytes
> > c = 230: OK, 1 bytes
> > c = 231: OK, 1 bytes
> > c = 232: OK, 1 bytes
> > c = 233: OK, 1 bytes
> > c = 234: OK, 1 bytes
> > c = 235: OK, 1 bytes
> > c = 236: OK, 1 bytes
> > c = 237: OK, 1 bytes
> > c = 238: OK, 1 bytes
> > c = 239: OK, 1 bytes
> > c = 240: OK, 1 bytes
> > c = 241: OK, 1 bytes
> > c = 242: OK, 1 bytes
> > c = 243: OK, 1 bytes
> > c = 244: OK, 1 bytes
> > c = 245: OK, 1 bytes
> > c = 246: OK, 1 bytes
> > c = 247: OK, 1 bytes
> > c = 248: OK, 1 bytes
> > c = 249: OK, 1 bytes
> > c = 250: OK, 1 bytes
> > c = 251: OK, 1 bytes
> > c = 252: OK, 1 bytes
> > c = 253: OK, 1 bytes
> > c = 254: OK, 1 bytes
> > c = 255: OK, 1 bytes
> > 
> > Actual output:
> > c = 1: OK, 1 bytes
> > c = 2: OK, 1 bytes
> > c = 3: OK, 1 bytes
> > c = 4: OK, 1 bytes
> > c = 5: OK, 1 bytes
> > c = 6: OK, 1 bytes
> > c = 7: OK, 1 bytes
> > c = 8: OK, 1 bytes
> > c = 9: OK, 1 bytes
> > c = 10: OK, 1 bytes
> > c = 11: OK, 1 bytes
> > c = 12: OK, 1 bytes
> > c = 13: OK, 1 bytes
> > c = 14: OK, 1 bytes
> > c = 15: OK, 1 bytes
> > c = 16: OK, 1 bytes
> > c = 17: OK, 1 bytes
> > c = 18: OK, 1 bytes
> > c = 19: OK, 1 bytes
> > c = 20: OK, 1 bytes
> > c = 21: OK, 1 bytes
> > c = 22: OK, 1 bytes
> > c = 23: OK, 1 bytes
> > c = 24: OK, 1 bytes
> > c = 25: OK, 1 bytes
> > c = 26: OK, 1 bytes
> > c = 27: OK, 1 bytes
> > c = 28: OK, 1 bytes
> > c = 29: OK, 1 bytes
> > c = 30: OK, 1 bytes
> > c = 31: OK, 1 bytes
> > c = 32: OK, 1 bytes
> > c = 33: OK, 1 bytes
> > c = 34: OK, 1 bytes
> > c = 35: OK, 1 bytes
> > c = 36: OK, 1 bytes
> > c = 37: OK, 1 bytes
> > c = 38: OK, 1 bytes
> > c = 39: OK, 1 bytes
> > c = 40: OK, 1 bytes
> > c = 41: OK, 1 bytes
> > c = 42: OK, 1 bytes
> > c = 43: OK, 1 bytes
> > c = 44: OK, 1 bytes
> > c = 45: OK, 1 bytes
> > c = 46: OK, 1 bytes
> > c = 47: OK, 1 bytes
> > c = 48: OK, 1 bytes
> > c = 49: OK, 1 bytes
> > c = 50: OK, 1 bytes
> > c = 51: OK, 1 bytes
> > c = 52: OK, 1 bytes
> > c = 53: OK, 1 bytes
> > c = 54: OK, 1 bytes
> > c = 55: OK, 1 bytes
> > c = 56: OK, 1 bytes
> > c = 57: OK, 1 bytes
> > c = 58: OK, 1 bytes
> > c = 59: OK, 1 bytes
> > c = 60: OK, 1 bytes
> > c = 61: OK, 1 bytes
> > c = 62: OK, 1 bytes
> > c = 63: OK, 1 bytes
> > c = 64: OK, 1 bytes
> > c = 65: OK, 1 bytes
> > c = 66: OK, 1 bytes
> > c = 67: OK, 1 bytes
> > c = 68: OK, 1 bytes
> > c = 69: OK, 1 bytes
> > c = 70: OK, 1 bytes
> > c = 71: OK, 1 bytes
> > c = 72: OK, 1 bytes
> > c = 73: OK, 1 bytes
> > c = 74: OK, 1 bytes
> > c = 75: OK, 1 bytes
> > c = 76: OK, 1 bytes
> > c = 77: OK, 1 bytes
> > c = 78: OK, 1 bytes
> > c = 79: OK, 1 bytes
> > c = 80: OK, 1 bytes
> > c = 81: OK, 1 bytes
> > c = 82: OK, 1 bytes
> > c = 83: OK, 1 bytes
> > c = 84: OK, 1 bytes
> > c = 85: OK, 1 bytes
> > c = 86: OK, 1 bytes
> > c = 87: OK, 1 bytes
> > c = 88: OK, 1 bytes
> > c = 89: OK, 1 bytes
> > c = 90: OK, 1 bytes
> > c = 91: OK, 1 bytes
> > c = 92: OK, 1 bytes
> > c = 93: OK, 1 bytes
> > c = 94: OK, 1 bytes
> > c = 95: OK, 1 bytes
> > c = 96: OK, 1 bytes
> > c = 97: OK, 1 bytes
> > c = 98: OK, 1 bytes
> > c = 99: OK, 1 bytes
> > c = 100: OK, 1 bytes
> > c = 101: OK, 1 bytes
> > c = 102: OK, 1 bytes
> > c = 103: OK, 1 bytes
> > c = 104: OK, 1 bytes
> > c = 105: OK, 1 bytes
> > c = 106: OK, 1 bytes
> > c = 107: OK, 1 bytes
> > c = 108: OK, 1 bytes
> > c = 109: OK, 1 bytes
> > c = 110: OK, 1 bytes
> > c = 111: OK, 1 bytes
> > c = 112: OK, 1 bytes
> > c = 113: OK, 1 bytes
> > c = 114: OK, 1 bytes
> > c = 115: OK, 1 bytes
> > c = 116: OK, 1 bytes
> > c = 117: OK, 1 bytes
> > c = 118: OK, 1 bytes
> > c = 119: OK, 1 bytes
> > c = 120: OK, 1 bytes
> > c = 121: OK, 1 bytes
> > c = 122: OK, 1 bytes
> > c = 123: OK, 1 bytes
> > c = 124: OK, 1 bytes
> > c = 125: OK, 1 bytes
> > c = 126: OK, 1 bytes
> > c = 127: OK, 1 bytes
> > c = 128: OK, 1 bytes
> > c = 129: OK, 1 bytes
> > c = 130: OK, 1 bytes
> > c = 131: OK, 1 bytes
> > c = 132: OK, 1 bytes
> > c = 133: OK, 1 bytes
> > c = 134: OK, 1 bytes
> > c = 135: OK, 1 bytes
> > c = 136: OK, 1 bytes
> > c = 137: OK, 1 bytes
> > c = 138: OK, 1 bytes
> > c = 139: OK, 1 bytes
> > c = 140: OK, 1 bytes
> > c = 141: OK, 1 bytes
> > c = 142: OK, 1 bytes
> > c = 143: OK, 1 bytes
> > c = 144: OK, 1 bytes
> > c = 145: OK, 1 bytes
> > c = 146: OK, 1 bytes
> > c = 147: OK, 1 bytes
> > c = 148: OK, 1 bytes
> > c = 149: OK, 1 bytes
> > c = 150: OK, 1 bytes
> > c = 151: OK, 1 bytes
> > c = 152: OK, 1 bytes
> > c = 153: OK, 1 bytes
> > c = 154: OK, 1 bytes
> > c = 155: OK, 1 bytes
> > c = 156: OK, 1 bytes
> > c = 157: OK, 1 bytes
> > c = 158: OK, 1 bytes
> > c = 159: OK, 1 bytes
> > c = 160: OK, 1 bytes
> > c = 161: OK, 1 bytes
> > c = 162: OK, 1 bytes
> > c = 163: OK, 1 bytes
> > c = 164: OK, 1 bytes
> > c = 165: OK, 1 bytes
> > c = 166: OK, 1 bytes
> > c = 167: OK, 1 bytes
> > c = 168: OK, 1 bytes
> > c = 169: OK, 1 bytes
> > c = 170: OK, 1 bytes
> > c = 171: OK, 1 bytes
> > c = 172: OK, 1 bytes
> > c = 173: OK, 1 bytes
> > c = 174: OK, 1 bytes
> > c = 175: OK, 1 bytes
> > c = 176: OK, 1 bytes
> > c = 177: OK, 1 bytes
> > c = 178: OK, 1 bytes
> > c = 179: OK, 1 bytes
> > c = 180: OK, 1 bytes
> > c = 181: OK, 1 bytes
> > c = 182: OK, 1 bytes
> > c = 183: OK, 1 bytes
> > c = 184: OK, 1 bytes
> > c = 185: OK, 1 bytes
> > c = 186: OK, 1 bytes
> > c = 187: OK, 1 bytes
> > c = 188: OK, 1 bytes
> > c = 189: OK, 1 bytes
> > c = 190: OK, 1 bytes
> > c = 191: OK, 1 bytes
> > c = 192: OK, 1 bytes
> > c = 193: OK, 1 bytes
> > c = 194: OK, 1 bytes
> > c = 195: OK, 1 bytes
> > c = 196: OK, 1 bytes
> > c = 197: OK, 1 bytes
> > c = 198: OK, 1 bytes
> > c = 199: OK, 1 bytes
> > c = 200: OK, 1 bytes
> > c = 201: OK, 1 bytes
> > c = 202: OK, 1 bytes
> > c = 203: OK, 1 bytes
> > c = 204: OK, 1 bytes
> > c = 205: OK, 1 bytes
> > c = 206: OK, 1 bytes
> > c = 207: OK, 1 bytes
> > c = 208: OK, 1 bytes
> > c = 209: OK, 1 bytes
> > c = 210: OK, 1 bytes
> > c = 211: OK, 1 bytes
> > c = 212: OK, 1 bytes
> > c = 213: OK, 1 bytes
> > c = 214: OK, 1 bytes
> > c = 215: OK, 1 bytes
> > c = 216: OK, 1 bytes
> > c = 217: OK, 1 bytes
> > c = 218: OK, 1 bytes
> > c = 219: OK, 1 bytes
> > c = 220: OK, 1 bytes
> > c = 221: OK, 1 bytes
> > c = 222: OK, 1 bytes
> > c = 223: OK, 1 bytes
> > c = 224: OK, 1 bytes
> > c = 225: OK, 1 bytes
> > c = 226: OK, 1 bytes
> > c = 227: OK, 1 bytes
> > c = 228: OK, 1 bytes
> > c = 229: OK, 1 bytes
> > c = 230: OK, 1 bytes
> > c = 231: OK, 1 bytes
> > c = 232: OK, 1 bytes
> > c = 233: OK, 1 bytes
> > c = 234: OK, 1 bytes
> > c = 235: OK, 1 bytes
> > c = 236: OK, 1 bytes
> > c = 237: OK, 1 bytes
> > c = 238: OK, 1 bytes
> > c = 239: OK, 1 bytes
> > c = 240: OK, 1 bytes
> > c = 241: OK, 1 bytes
> > c = 242: OK, 1 bytes
> > c = 243: OK, 1 bytes
> > c = 244: OK, 1 bytes
> > c = 245: OK, 1 bytes
> > c = 246: OK, 1 bytes
> > c = 247: OK, 1 bytes
> > c = 248: OK, 1 bytes
> > c = 249: OK, 1 bytes
> > c = 250: OK, 1 bytes
> > c = 251: OK, 1 bytes
> > c = 252: OK, 1 bytes
> > c = 253: OK, 1 bytes
> > c = 254: OK, 1 bytes
> > c = 255: swprintf failed: Illegal byte sequence
> > 
> > This is a bug, because POSIX says that in the C / POSIX locale, "all byte
> > values are valid characters" [1].
> 
> Yes, this is a bug and seems to be an instance of mishandling of
> signed conversions. Attached should correct it.
> 
> Rich

> diff --git a/src/stdio/vfwprintf.c b/src/stdio/vfwprintf.c
> index 53697701..a653e233 100644
> --- a/src/stdio/vfwprintf.c
> +++ b/src/stdio/vfwprintf.c
> @@ -271,7 +271,7 @@ static int wprintf_core(FILE *f, const wchar_t *fmt, va_list *ap, union arg *nl_
>  		case 'C':
>  			if (w<1) w=1;
>  			pad(f, w-1, fl);
> -			out(f, &(wchar_t){t=='C' ? arg.i : btowc(arg.i)}, 1);
> +			out(f, &(wchar_t){t=='C' ? arg.i : btowc(arg.i & 0xff)}, 1);
>  			pad(f, w-1, fl^LEFT_ADJ);
>  			l = w;
>  			continue;

Hmm -- while this works, formally, %c takes an argument of type int
not char, so I think we should probably actually change the state
machine for both narrow and wide printf to terminate with state INT
rather than CHAR for 'c'. And likewise, %lc/%C takes wint_t, which has
type unsigned, so although it doesn't matter the state machine should
terminate with state UINT rather than INT (wint_t is unsigned).

Rich

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.