Skip to content

Commit 3fbcf75

Browse files
nivedita76ardbiesheuvel
authored andcommitted
efi/printf: Factor out width/precision parsing
Factor out the width/precision parsing into a helper function. Signed-off-by: Arvind Sankar <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Ard Biesheuvel <[email protected]>
1 parent 7c30fd7 commit 3fbcf75

File tree

1 file changed

+40
-21
lines changed

1 file changed

+40
-21
lines changed

drivers/firmware/efi/libstub/vsprintf.c

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,20 @@ int get_flags(const char **fmt)
231231
} while (1);
232232
}
233233

234-
int vsprintf(char *buf, const char *fmt, va_list args)
234+
static
235+
int get_int(const char **fmt, va_list *ap)
236+
{
237+
if (isdigit(**fmt))
238+
return skip_atoi(fmt);
239+
if (**fmt == '*') {
240+
++(*fmt);
241+
/* it's the next argument */
242+
return va_arg(*ap, int);
243+
}
244+
return 0;
245+
}
246+
247+
int vsprintf(char *buf, const char *fmt, va_list ap)
235248
{
236249
int len;
237250
unsigned long long num;
@@ -246,6 +259,24 @@ int vsprintf(char *buf, const char *fmt, va_list args)
246259
number of chars for from string */
247260
int qualifier; /* 'h', 'hh', 'l' or 'll' for integer fields */
248261

262+
va_list args;
263+
264+
/*
265+
* We want to pass our input va_list to helper functions by reference,
266+
* but there's an annoying edge case. If va_list was originally passed
267+
* to us by value, we could just pass &ap down to the helpers. This is
268+
* the case on, for example, X86_32.
269+
* However, on X86_64 (and possibly others), va_list is actually a
270+
* size-1 array containing a structure. Our function parameter ap has
271+
* decayed from T[1] to T*, and &ap has type T** rather than T(*)[1],
272+
* which is what will be expected by a function taking a va_list *
273+
* parameter.
274+
* One standard way to solve this mess is by creating a copy in a local
275+
* variable of type va_list and then passing a pointer to that local
276+
* copy instead, which is what we do here.
277+
*/
278+
va_copy(args, ap);
279+
249280
for (str = buf; *fmt; ++fmt) {
250281
if (*fmt != '%' || *++fmt == '%') {
251282
*str++ = *fmt;
@@ -256,32 +287,17 @@ int vsprintf(char *buf, const char *fmt, va_list args)
256287
flags = get_flags(&fmt);
257288

258289
/* get field width */
259-
field_width = -1;
260-
if (isdigit(*fmt)) {
261-
field_width = skip_atoi(&fmt);
262-
} else if (*fmt == '*') {
263-
++fmt;
264-
/* it's the next argument */
265-
field_width = va_arg(args, int);
266-
if (field_width < 0) {
267-
field_width = -field_width;
268-
flags |= LEFT;
269-
}
290+
field_width = get_int(&fmt, &args);
291+
if (field_width < 0) {
292+
field_width = -field_width;
293+
flags |= LEFT;
270294
}
271295

272296
/* get the precision */
273297
precision = -1;
274298
if (*fmt == '.') {
275299
++fmt;
276-
if (isdigit(*fmt)) {
277-
precision = skip_atoi(&fmt);
278-
} else if (*fmt == '*') {
279-
++fmt;
280-
/* it's the next argument */
281-
precision = va_arg(args, int);
282-
} else {
283-
precision = 0;
284-
}
300+
precision = get_int(&fmt, &args);
285301
if (precision >= 0)
286302
flags &= ~ZEROPAD;
287303
}
@@ -392,6 +408,9 @@ int vsprintf(char *buf, const char *fmt, va_list args)
392408
str = number(str, num, base, field_width, precision, flags);
393409
}
394410
*str = '\0';
411+
412+
va_end(args);
413+
395414
return str - buf;
396415
}
397416

0 commit comments

Comments
 (0)