Skip to content

Commit 15c316b

Browse files
nivedita76ardbiesheuvel
authored andcommitted
efi/libstub: Get the exact UTF-8 length
efi_convert_cmdline currently overestimates the length of the equivalent UTF-8 encoding. snprintf can now be used to do the conversion to UTF-8, however, it does not have a way to specify the size of the UTF-16 string, only the size of the resulting UTF-8 string. So in order to use it, we need to precalculate the exact UTF-8 size. Signed-off-by: Arvind Sankar <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Ard Biesheuvel <[email protected]>
1 parent a713979 commit 15c316b

File tree

1 file changed

+32
-12
lines changed

1 file changed

+32
-12
lines changed

drivers/firmware/efi/libstub/efi-stub-helper.c

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -205,15 +205,6 @@ efi_status_t efi_parse_options(char const *cmdline)
205205
return EFI_SUCCESS;
206206
}
207207

208-
/*
209-
* Get the number of UTF-8 bytes corresponding to an UTF-16 character.
210-
* This overestimates for surrogates, but that is okay.
211-
*/
212-
static int efi_utf8_bytes(u16 c)
213-
{
214-
return 1 + (c >= 0x80) + (c >= 0x800);
215-
}
216-
217208
/*
218209
* Convert an UTF-16 string, not necessarily null terminated, to UTF-8.
219210
*/
@@ -274,10 +265,39 @@ char *efi_convert_cmdline(efi_loaded_image_t *image,
274265

275266
if (options) {
276267
s2 = options;
277-
while (*s2 && *s2 != '\n'
278-
&& options_chars < load_options_chars) {
279-
options_bytes += efi_utf8_bytes(*s2++);
268+
while (options_chars < load_options_chars) {
269+
u16 c = *s2++;
270+
271+
if (c == L'\0' || c == L'\n')
272+
break;
273+
/*
274+
* Get the number of UTF-8 bytes corresponding to a
275+
* UTF-16 character.
276+
* The first part handles everything in the BMP.
277+
*/
278+
options_bytes += 1 + (c >= 0x80) + (c >= 0x800);
280279
options_chars++;
280+
/*
281+
* Add one more byte for valid surrogate pairs. Invalid
282+
* surrogates will be replaced with 0xfffd and take up
283+
* only 3 bytes.
284+
*/
285+
if ((c & 0xfc00) == 0xd800) {
286+
/*
287+
* If the very last word is a high surrogate,
288+
* we must ignore it since we can't access the
289+
* low surrogate.
290+
*/
291+
if (options_chars == load_options_chars) {
292+
options_bytes -= 3;
293+
options_chars--;
294+
break;
295+
} else if ((*s2 & 0xfc00) == 0xdc00) {
296+
options_bytes++;
297+
options_chars++;
298+
s2++;
299+
}
300+
}
281301
}
282302
}
283303

0 commit comments

Comments
 (0)