2011-05-13 Paul Eggert <eggert@cs.ucla.edu>
+ * editfns.c (Fformat): Fix several integer overflow problems.
+ For example, without this change, (format "%2147483648d" 1) dumps
+ core on x86-64 GNU/Linux. Use EMACS_INT, not size_t, for sizes,
+ since we prefer using signed values, and EMACS_INT will be big
+ enough soon, even on 32-bit hosts. Also, prefer EMACS_INT to int
+ for sizes. Don't assume that pI is either "l" or ""; it might be
+ "ll" or "I64". Check for width and precision greater than
+ INT_MAX, as this can make sprintf go kaflooey. (Bug#8668)
+
* dispextern.h (struct image): Don't assume time_t <= unsigned long.
* image.c (clear_image_cache): Likewise.
usage: (format STRING &rest OBJECTS) */)
(size_t nargs, register Lisp_Object *args)
{
- register size_t n; /* The number of the next arg to substitute */
- register size_t total; /* An estimate of the final length */
+ register EMACS_INT n; /* The number of the next arg to substitute */
+ register EMACS_INT total; /* An estimate of the final length */
+ int pIlen = sizeof pI - 1;
char *buf, *p;
register char *format, *end, *format_start;
- int nchars;
+ EMACS_INT nchars;
/* Nonzero if the output should be a multibyte string,
which is true if any of the inputs is one. */
int multibyte = 0;
no argument, *will* be assigned to in the case that a `%' and `.'
occur after the final format specifier. */
int *precision = (int *) (alloca ((nargs + 1) * sizeof (int)));
- int longest_format;
+ EMACS_INT longest_format;
Lisp_Object val;
int arg_intervals = 0;
USE_SAFE_ALLOCA;
info[0] is unused. Unused elements have -1 for start. */
struct info
{
- int start, end, intervals;
+ EMACS_INT start, end;
+ int intervals;
} *info = 0;
/* It should not be necessary to GCPRO ARGS, because
/* Allocate the info and discarded tables. */
{
- size_t nbytes = (nargs+1) * sizeof *info;
- size_t i;
+ EMACS_INT nbytes = (nargs + 1) * sizeof *info;
+ EMACS_INT i;
if (!info)
info = (struct info *) alloca (nbytes);
memset (info, 0, nbytes);
|| * format == ' ' || *format == '+'))
++format;
+ /* Parse width and precision, limiting them to the range of 'int'
+ because otherwise the underyling sprintf may go kaflooey. */
+
if (*format >= '0' && *format <= '9')
{
- for (field_width = 0; *format >= '0' && *format <= '9'; ++format)
- field_width = 10 * field_width + *format - '0';
+ char *width_end;
+ unsigned long width = strtoul (format, &width_end, 10);
+ if (INT_MAX < width)
+ error ("Format string field width too large");
+ field_width = width;
+ format = width_end;
}
/* N is not incremented for another few lines below, so refer to
element N+1 (which might be precision[NARGS]). */
if (*format == '.')
{
- ++format;
- for (precision[n+1] = 0; *format >= '0' && *format <= '9'; ++format)
- precision[n+1] = 10 * precision[n+1] + *format - '0';
+ char *prec_end;
+ unsigned long prec = strtoul (format + 1, &prec_end, 10);
+ if (INT_MAX < prec)
+ error ("Format string precision too large");
+ precision[n + 1] = prec;
+ format = prec_end;
}
- /* Extra +1 for 'l' that we may need to insert into the
- format. */
- if (format - this_format_start + 2 > longest_format)
- longest_format = format - this_format_start + 2;
+ if (longest_format < format - this_format_start + pIlen + 1)
+ longest_format = format - this_format_start + pIlen + 1;
if (format == end)
error ("Format string ends in middle of format specifier");
}
else if (INTEGERP (args[n]) || FLOATP (args[n]))
{
- int this_nchars;
+ EMACS_INT this_nchars;
+ EMACS_INT this_format_len = format - this_format_start;
- memcpy (this_format, this_format_start,
- format - this_format_start);
- this_format[format - this_format_start] = 0;
+ memcpy (this_format, this_format_start, this_format_len);
+ this_format[this_format_len] = 0;
if (format[-1] == 'e' || format[-1] == 'f' || format[-1] == 'g')
sprintf (p, this_format, XFLOAT_DATA (args[n]));
else
{
- if (sizeof (EMACS_INT) > sizeof (int)
- && format[-1] != 'c')
+ if (pIlen && format[-1] != 'c')
{
- /* Insert 'l' before format spec. */
- this_format[format - this_format_start]
- = this_format[format - this_format_start - 1];
- this_format[format - this_format_start - 1] = 'l';
- this_format[format - this_format_start + 1] = 0;
+ /* Insert pI before format spec. */
+ memcpy (&this_format[this_format_len - 1], pI, pIlen);
+ this_format[this_format_len + pIlen - 1] = format[-1];
+ this_format[this_format_len + pIlen] = 0;
}
if (INTEGERP (args[n]))
if (CONSP (props))
{
EMACS_INT bytepos = 0, position = 0, translated = 0;
- int argn = 1;
+ EMACS_INT argn = 1;
Lisp_Object list;
/* Adjust the bounds of each text property