Eliminate variable length arrays in grub_vsnprintf_real.
A bit tricky because this function has to continue to work without heap for short strings. Fixing prealloc to 32 arguments is reasonable but make all stack references use 32-bit offset rather than 8-bit one. So split va_args preparsing to separate function and put the prealloc into the caller.
This commit is contained in:
parent
4f9541226c
commit
2d76b4d81e
2 changed files with 180 additions and 128 deletions
10
ChangeLog
10
ChangeLog
|
@ -1,3 +1,13 @@
|
||||||
|
2013-11-27 Vladimir Serbinenko <phcoder@gmail.com>
|
||||||
|
|
||||||
|
Eliminate variable length arrays in grub_vsnprintf_real.
|
||||||
|
|
||||||
|
A bit tricky because this function has to continue to work without
|
||||||
|
heap for short strings. Fixing prealloc to 32 arguments is reasonable
|
||||||
|
but make all stack references use 32-bit offset rather than 8-bit one.
|
||||||
|
So split va_args preparsing to separate function and put the prealloc
|
||||||
|
into the caller.
|
||||||
|
|
||||||
2013-11-27 Vladimir Serbinenko <phcoder@gmail.com>
|
2013-11-27 Vladimir Serbinenko <phcoder@gmail.com>
|
||||||
|
|
||||||
Introduce grub_util_file_sync and use it instead of fsync(fileno(f)).
|
Introduce grub_util_file_sync and use it instead of fsync(fileno(f)).
|
||||||
|
|
|
@ -25,8 +25,39 @@
|
||||||
#include <grub/env.h>
|
#include <grub/env.h>
|
||||||
#include <grub/i18n.h>
|
#include <grub/i18n.h>
|
||||||
|
|
||||||
|
union printf_arg
|
||||||
|
{
|
||||||
|
/* Yes, type is also part of union as the moment we fill the value
|
||||||
|
we don't need to store its type anymore (when we'll need it, we'll
|
||||||
|
have format spec again. So save some space. */
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
INT, LONG, LONGLONG,
|
||||||
|
UNSIGNED_INT = 3, UNSIGNED_LONG, UNSIGNED_LONGLONG
|
||||||
|
} type;
|
||||||
|
long long ll;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct printf_args
|
||||||
|
{
|
||||||
|
union printf_arg prealloc[32];
|
||||||
|
union printf_arg *ptr;
|
||||||
|
grub_size_t count;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
parse_printf_args (const char *fmt0, struct printf_args *args,
|
||||||
|
va_list args_in);
|
||||||
static int
|
static int
|
||||||
grub_vsnprintf_real (char *str, grub_size_t n, const char *fmt, va_list args);
|
grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt0,
|
||||||
|
struct printf_args *args);
|
||||||
|
|
||||||
|
static void
|
||||||
|
free_printf_args (struct printf_args *args)
|
||||||
|
{
|
||||||
|
if (args->ptr != args->prealloc)
|
||||||
|
grub_free (args->ptr);
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
grub_iswordseparator (int c)
|
grub_iswordseparator (int c)
|
||||||
|
@ -169,15 +200,16 @@ grub_real_dprintf (const char *file, const int line, const char *condition,
|
||||||
#define PREALLOC_SIZE 255
|
#define PREALLOC_SIZE 255
|
||||||
|
|
||||||
int
|
int
|
||||||
grub_vprintf (const char *fmt, va_list args)
|
grub_vprintf (const char *fmt, va_list ap)
|
||||||
{
|
{
|
||||||
grub_size_t s;
|
grub_size_t s;
|
||||||
static char buf[PREALLOC_SIZE + 1];
|
static char buf[PREALLOC_SIZE + 1];
|
||||||
char *curbuf = buf;
|
char *curbuf = buf;
|
||||||
va_list ap2;
|
struct printf_args args;
|
||||||
va_copy (ap2, args);
|
|
||||||
|
|
||||||
s = grub_vsnprintf_real (buf, PREALLOC_SIZE, fmt, args);
|
parse_printf_args (fmt, &args, ap);
|
||||||
|
|
||||||
|
s = grub_vsnprintf_real (buf, PREALLOC_SIZE, fmt, &args);
|
||||||
if (s > PREALLOC_SIZE)
|
if (s > PREALLOC_SIZE)
|
||||||
{
|
{
|
||||||
curbuf = grub_malloc (s + 1);
|
curbuf = grub_malloc (s + 1);
|
||||||
|
@ -191,16 +223,16 @@ grub_vprintf (const char *fmt, va_list args)
|
||||||
curbuf = buf;
|
curbuf = buf;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
s = grub_vsnprintf_real (curbuf, s, fmt, ap2);
|
s = grub_vsnprintf_real (curbuf, s, fmt, &args);
|
||||||
}
|
}
|
||||||
|
|
||||||
va_end (ap2);
|
free_printf_args (&args);
|
||||||
|
|
||||||
grub_xputs (curbuf);
|
grub_xputs (curbuf);
|
||||||
|
|
||||||
if (curbuf != buf)
|
if (curbuf != buf)
|
||||||
grub_free (curbuf);
|
grub_free (curbuf);
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -724,7 +756,7 @@ __umoddi3 (grub_uint64_t a, grub_uint64_t b)
|
||||||
|
|
||||||
/* Convert a long long value to a string. This function avoids 64-bit
|
/* Convert a long long value to a string. This function avoids 64-bit
|
||||||
modular arithmetic or divisions. */
|
modular arithmetic or divisions. */
|
||||||
static char *
|
static inline char *
|
||||||
grub_lltoa (char *str, int c, unsigned long long n)
|
grub_lltoa (char *str, int c, unsigned long long n)
|
||||||
{
|
{
|
||||||
unsigned base = (c == 'x') ? 16 : 10;
|
unsigned base = (c == 'x') ? 16 : 10;
|
||||||
|
@ -762,39 +794,21 @@ grub_lltoa (char *str, int c, unsigned long long n)
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static void
|
||||||
write_char (char *str, grub_size_t *count, grub_size_t max_len, unsigned char ch)
|
parse_printf_args (const char *fmt0, struct printf_args *args,
|
||||||
{
|
va_list args_in)
|
||||||
if (*count < max_len)
|
|
||||||
str[*count] = ch;
|
|
||||||
|
|
||||||
(*count)++;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
write_str (char *str, grub_size_t *count, grub_size_t max_len, const char *s)
|
|
||||||
{
|
|
||||||
while (*s)
|
|
||||||
write_char (str, count, max_len, *s++);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
write_fill (char *str, grub_size_t *count, grub_size_t max_len, const char ch, int count_fill)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < count_fill; i++)
|
|
||||||
write_char (str, count, max_len, ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
|
||||||
grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt0, va_list args_in)
|
|
||||||
{
|
{
|
||||||
|
const char *fmt;
|
||||||
char c;
|
char c;
|
||||||
grub_size_t n = 0;
|
grub_size_t n = 0;
|
||||||
grub_size_t count = 0;
|
|
||||||
grub_size_t count_args = 0;
|
args->count = 0;
|
||||||
const char *fmt;
|
|
||||||
|
COMPILE_TIME_ASSERT (sizeof (int) == sizeof (grub_uint32_t));
|
||||||
|
COMPILE_TIME_ASSERT (sizeof (int) <= sizeof (long long));
|
||||||
|
COMPILE_TIME_ASSERT (sizeof (long) <= sizeof (long long));
|
||||||
|
COMPILE_TIME_ASSERT (sizeof (long long) == sizeof (void *)
|
||||||
|
|| sizeof (int) == sizeof (void *));
|
||||||
|
|
||||||
fmt = fmt0;
|
fmt = fmt0;
|
||||||
while ((c = *fmt++) != 0)
|
while ((c = *fmt++) != 0)
|
||||||
|
@ -838,30 +852,31 @@ grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt0, va_list a
|
||||||
case 'c':
|
case 'c':
|
||||||
case 'C':
|
case 'C':
|
||||||
case 's':
|
case 's':
|
||||||
count_args++;
|
args->count++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum { INT, LONG, LONGLONG, POINTER } types[count_args];
|
if (args->count <= ARRAY_SIZE (args->prealloc))
|
||||||
union
|
args->ptr = args->prealloc;
|
||||||
{
|
else
|
||||||
int i;
|
{
|
||||||
long l;
|
args->ptr = grub_malloc (args->count * sizeof (args->ptr[0]));
|
||||||
long long ll;
|
if (!args->ptr)
|
||||||
void *p;
|
{
|
||||||
} args[count_args];
|
grub_errno = GRUB_ERR_NONE;
|
||||||
|
args->ptr = args->prealloc;
|
||||||
|
args->count = ARRAY_SIZE (args->prealloc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
COMPILE_TIME_ASSERT (sizeof (int) == sizeof (grub_uint32_t));
|
grub_memset (args->ptr, 0, args->count * sizeof (args->ptr[0]));
|
||||||
|
|
||||||
grub_memset (types, 0, sizeof (types));
|
|
||||||
|
|
||||||
fmt = fmt0;
|
fmt = fmt0;
|
||||||
n = 0;
|
n = 0;
|
||||||
while ((c = *fmt++) != 0)
|
while ((c = *fmt++) != 0)
|
||||||
{
|
{
|
||||||
int longfmt = 0;
|
int longfmt = 0;
|
||||||
int longlongfmt = 0;
|
|
||||||
grub_size_t curn;
|
grub_size_t curn;
|
||||||
const char *p;
|
const char *p;
|
||||||
|
|
||||||
|
@ -901,69 +916,87 @@ grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt0, va_list a
|
||||||
{
|
{
|
||||||
c = *fmt++;
|
c = *fmt++;
|
||||||
longfmt = 1;
|
longfmt = 1;
|
||||||
if (c == 'l')
|
|
||||||
{
|
|
||||||
c = *fmt++;
|
|
||||||
longlongfmt = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (curn >= count_args)
|
if (c == 'l')
|
||||||
|
{
|
||||||
|
c = *fmt++;
|
||||||
|
longfmt = 2;
|
||||||
|
}
|
||||||
|
if (curn >= args->count)
|
||||||
continue;
|
continue;
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
case 'x':
|
case 'x':
|
||||||
case 'u':
|
case 'u':
|
||||||
|
args->ptr[curn].type = UNSIGNED_INT + longfmt;
|
||||||
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
if (longlongfmt)
|
args->ptr[curn].type = INT + longfmt;
|
||||||
types[curn] = LONGLONG;
|
|
||||||
else if (longfmt)
|
|
||||||
types[curn] = LONG;
|
|
||||||
else
|
|
||||||
types[curn] = INT;
|
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
case 's':
|
case 's':
|
||||||
types[curn] = POINTER;
|
if (sizeof (void *) == sizeof (long long))
|
||||||
|
args->ptr[curn].type = UNSIGNED_LONGLONG;
|
||||||
|
else
|
||||||
|
args->ptr[curn].type = UNSIGNED_INT;
|
||||||
break;
|
break;
|
||||||
case 'C':
|
case 'C':
|
||||||
case 'c':
|
case 'c':
|
||||||
types[curn] = INT;
|
args->ptr[curn].type = INT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (n = 0; n < count_args; n++)
|
for (n = 0; n < args->count; n++)
|
||||||
switch (types[n])
|
switch (args->ptr[n].type)
|
||||||
{
|
{
|
||||||
case POINTER:
|
|
||||||
args[n].p = va_arg (args_in, void *);
|
|
||||||
break;
|
|
||||||
case INT:
|
case INT:
|
||||||
args[n].i = va_arg (args_in, int);
|
args->ptr[n].ll = va_arg (args_in, int);
|
||||||
break;
|
break;
|
||||||
case LONG:
|
case LONG:
|
||||||
args[n].l = va_arg (args_in, long);
|
args->ptr[n].ll = va_arg (args_in, long);
|
||||||
|
break;
|
||||||
|
case UNSIGNED_INT:
|
||||||
|
args->ptr[n].ll = va_arg (args_in, unsigned int);
|
||||||
|
break;
|
||||||
|
case UNSIGNED_LONG:
|
||||||
|
args->ptr[n].ll = va_arg (args_in, unsigned long);
|
||||||
break;
|
break;
|
||||||
case LONGLONG:
|
case LONGLONG:
|
||||||
args[n].ll = va_arg (args_in, long long);
|
case UNSIGNED_LONGLONG:
|
||||||
|
args->ptr[n].ll = va_arg (args_in, long long);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void __attribute__ ((always_inline))
|
||||||
|
write_char (char *str, grub_size_t *count, grub_size_t max_len, unsigned char ch)
|
||||||
|
{
|
||||||
|
if (*count < max_len)
|
||||||
|
str[*count] = ch;
|
||||||
|
|
||||||
|
(*count)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt0,
|
||||||
|
struct printf_args *args)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
grub_size_t n = 0;
|
||||||
|
grub_size_t count = 0;
|
||||||
|
const char *fmt;
|
||||||
|
|
||||||
fmt = fmt0;
|
fmt = fmt0;
|
||||||
|
|
||||||
n = 0;
|
|
||||||
while ((c = *fmt++) != 0)
|
while ((c = *fmt++) != 0)
|
||||||
{
|
{
|
||||||
char tmp[32];
|
|
||||||
unsigned int format1 = 0;
|
unsigned int format1 = 0;
|
||||||
unsigned int format2 = ~ 0U;
|
unsigned int format2 = ~ 0U;
|
||||||
char zerofill = ' ';
|
char zerofill = ' ';
|
||||||
int rightfill = 0;
|
char rightfill = 0;
|
||||||
int longfmt = 0;
|
|
||||||
int longlongfmt = 0;
|
|
||||||
int unsig = 0;
|
|
||||||
grub_size_t curn;
|
grub_size_t curn;
|
||||||
|
|
||||||
if (c != '%')
|
if (c != '%')
|
||||||
{
|
{
|
||||||
write_char (str, &count, max_len,c);
|
write_char (str, &count, max_len,c);
|
||||||
|
@ -1008,15 +1041,9 @@ grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt0, va_list a
|
||||||
|
|
||||||
c = *fmt++;
|
c = *fmt++;
|
||||||
if (c == 'l')
|
if (c == 'l')
|
||||||
{
|
c = *fmt++;
|
||||||
longfmt = 1;
|
if (c == 'l')
|
||||||
c = *fmt++;
|
c = *fmt++;
|
||||||
if (c == 'l')
|
|
||||||
{
|
|
||||||
longlongfmt = 1;
|
|
||||||
c = *fmt++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c == '%')
|
if (c == '%')
|
||||||
{
|
{
|
||||||
|
@ -1024,45 +1051,47 @@ grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt0, va_list a
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (curn >= count_args)
|
if (curn >= args->count)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
long long curarg = args->ptr[curn].ll;
|
||||||
|
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
case 'p':
|
case 'p':
|
||||||
write_str (str, &count, max_len, "0x");
|
write_char (str, &count, max_len, '0');
|
||||||
|
write_char (str, &count, max_len, 'x');
|
||||||
c = 'x';
|
c = 'x';
|
||||||
longlongfmt |= (sizeof (void *) == sizeof (long long));
|
|
||||||
/* Fall through. */
|
/* Fall through. */
|
||||||
case 'x':
|
case 'x':
|
||||||
case 'u':
|
case 'u':
|
||||||
unsig = 1;
|
|
||||||
/* Fall through. */
|
|
||||||
case 'd':
|
case 'd':
|
||||||
if (longlongfmt)
|
{
|
||||||
grub_lltoa (tmp, c, args[curn].ll);
|
char tmp[32];
|
||||||
else if (longfmt && unsig)
|
const char *p = tmp;
|
||||||
grub_lltoa (tmp, c, (unsigned long) args[curn].l);
|
grub_size_t len;
|
||||||
else if (longfmt)
|
grub_size_t fill;
|
||||||
grub_lltoa (tmp, c, args[curn].l);
|
|
||||||
else if (unsig)
|
len = grub_lltoa (tmp, c, curarg) - tmp;
|
||||||
grub_lltoa (tmp, c, (unsigned) args[curn].i);
|
fill = len < format1 ? format1 - len : 0;
|
||||||
else
|
if (! rightfill)
|
||||||
grub_lltoa (tmp, c, args[curn].i);
|
while (fill--)
|
||||||
if (! rightfill && grub_strlen (tmp) < format1)
|
write_char (str, &count, max_len, zerofill);
|
||||||
write_fill (str, &count, max_len, zerofill, format1 - grub_strlen (tmp));
|
while (*p)
|
||||||
write_str (str, &count, max_len, tmp);
|
write_char (str, &count, max_len, *p++);
|
||||||
if (rightfill && grub_strlen (tmp) < format1)
|
if (rightfill)
|
||||||
write_fill (str, &count, max_len, zerofill, format1 - grub_strlen (tmp));
|
while (fill--)
|
||||||
|
write_char (str, &count, max_len, zerofill);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'c':
|
case 'c':
|
||||||
write_char (str, &count, max_len,args[curn].i & 0xff);
|
write_char (str, &count, max_len,curarg & 0xff);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'C':
|
case 'C':
|
||||||
{
|
{
|
||||||
grub_uint32_t code = args[curn].i;
|
grub_uint32_t code = curarg;
|
||||||
int shift;
|
int shift;
|
||||||
unsigned mask;
|
unsigned mask;
|
||||||
|
|
||||||
|
@ -1103,20 +1132,25 @@ grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt0, va_list a
|
||||||
case 's':
|
case 's':
|
||||||
{
|
{
|
||||||
grub_size_t len = 0;
|
grub_size_t len = 0;
|
||||||
const char *p = args[curn].p ? : "(null)";
|
grub_size_t fill;
|
||||||
|
const char *p = ((char *) (grub_addr_t) curarg) ? : "(null)";
|
||||||
|
grub_size_t i;
|
||||||
|
|
||||||
while (len < format2 && p[len])
|
while (len < format2 && p[len])
|
||||||
len++;
|
len++;
|
||||||
|
|
||||||
if (!rightfill && len < format1)
|
fill = len < format1 ? format1 - len : 0;
|
||||||
write_fill (str, &count, max_len, zerofill, format1 - len);
|
|
||||||
|
if (!rightfill)
|
||||||
|
while (fill--)
|
||||||
|
write_char (str, &count, max_len, zerofill);
|
||||||
|
|
||||||
grub_size_t i;
|
|
||||||
for (i = 0; i < len; i++)
|
for (i = 0; i < len; i++)
|
||||||
write_char (str, &count, max_len,*p++);
|
write_char (str, &count, max_len,*p++);
|
||||||
|
|
||||||
if (rightfill && len < format1)
|
if (rightfill)
|
||||||
write_fill (str, &count, max_len, zerofill, format1 - len);
|
while (fill--)
|
||||||
|
write_char (str, &count, max_len, zerofill);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -1131,7 +1165,6 @@ grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt0, va_list a
|
||||||
str[count] = '\0';
|
str[count] = '\0';
|
||||||
else
|
else
|
||||||
str[max_len] = '\0';
|
str[max_len] = '\0';
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1139,13 +1172,18 @@ int
|
||||||
grub_vsnprintf (char *str, grub_size_t n, const char *fmt, va_list ap)
|
grub_vsnprintf (char *str, grub_size_t n, const char *fmt, va_list ap)
|
||||||
{
|
{
|
||||||
grub_size_t ret;
|
grub_size_t ret;
|
||||||
|
struct printf_args args;
|
||||||
|
|
||||||
if (!n)
|
if (!n)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
n--;
|
n--;
|
||||||
|
|
||||||
ret = grub_vsnprintf_real (str, n, fmt, ap);
|
parse_printf_args (fmt, &args, ap);
|
||||||
|
|
||||||
|
ret = grub_vsnprintf_real (str, n, fmt, &args);
|
||||||
|
|
||||||
|
free_printf_args (&args);
|
||||||
|
|
||||||
return ret < n ? ret : n;
|
return ret < n ? ret : n;
|
||||||
}
|
}
|
||||||
|
@ -1168,22 +1206,26 @@ grub_xvasprintf (const char *fmt, va_list ap)
|
||||||
{
|
{
|
||||||
grub_size_t s, as = PREALLOC_SIZE;
|
grub_size_t s, as = PREALLOC_SIZE;
|
||||||
char *ret;
|
char *ret;
|
||||||
|
struct printf_args args;
|
||||||
|
|
||||||
|
parse_printf_args (fmt, &args, ap);
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
va_list ap2;
|
|
||||||
ret = grub_malloc (as + 1);
|
ret = grub_malloc (as + 1);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return NULL;
|
{
|
||||||
|
free_printf_args (&args);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
va_copy (ap2, ap);
|
s = grub_vsnprintf_real (ret, as, fmt, &args);
|
||||||
|
|
||||||
s = grub_vsnprintf_real (ret, as, fmt, ap2);
|
|
||||||
|
|
||||||
va_end (ap2);
|
|
||||||
|
|
||||||
if (s <= as)
|
if (s <= as)
|
||||||
return ret;
|
{
|
||||||
|
free_printf_args (&args);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
grub_free (ret);
|
grub_free (ret);
|
||||||
as = s;
|
as = s;
|
||||||
|
|
Loading…
Reference in a new issue