Marc-Andre Lemburg <mal@lemburg.com>:

New buffer overflow checks for formatting strings.

By Trent Mick.
This commit is contained in:
Marc-André Lemburg 2000-06-30 10:29:57 +00:00
parent 587794b386
commit f28dd83b86
2 changed files with 129 additions and 49 deletions

View File

@ -124,8 +124,13 @@ PyObject *
PyString_FromString(str) PyString_FromString(str)
const char *str; const char *str;
{ {
register unsigned int size = strlen(str); register size_t size = strlen(str);
register PyStringObject *op; register PyStringObject *op;
if (size > INT_MAX) {
PyErr_SetString(PyExc_OverflowError,
"string is too long for a Python string");
return NULL;
}
#ifndef DONT_SHARE_SHORT_STRINGS #ifndef DONT_SHARE_SHORT_STRINGS
if (size == 0 && (op = nullstring) != NULL) { if (size == 0 && (op = nullstring) != NULL) {
#ifdef COUNT_ALLOCS #ifdef COUNT_ALLOCS
@ -237,9 +242,13 @@ static PyObject *
string_repr(op) string_repr(op)
register PyStringObject *op; register PyStringObject *op;
{ {
/* XXX overflow? */ size_t newsize = 2 + 4 * op->ob_size * sizeof(char);
int newsize = 2 + 4 * op->ob_size * sizeof(char); PyObject *v;
PyObject *v = PyString_FromStringAndSize((char *)NULL, newsize); if (newsize > INT_MAX) {
PyErr_SetString(PyExc_OverflowError,
"string is too large to make repr");
}
v = PyString_FromStringAndSize((char *)NULL, newsize);
if (v == NULL) { if (v == NULL) {
return NULL; return NULL;
} }
@ -2335,36 +2344,52 @@ getnextarg(args, arglen, p_argidx)
#define F_ZERO (1<<4) #define F_ZERO (1<<4)
static int static int
formatfloat(buf, flags, prec, type, v) formatfloat(buf, buflen, flags, prec, type, v)
char *buf; char *buf;
size_t buflen;
int flags; int flags;
int prec; int prec;
int type; int type;
PyObject *v; PyObject *v;
{ {
/* fmt = '%#.' + `prec` + `type`
worst case length = 3 + 10 (len of INT_MAX) + 1 = 14 (use 20)*/
char fmt[20]; char fmt[20];
double x; double x;
if (!PyArg_Parse(v, "d;float argument required", &x)) if (!PyArg_Parse(v, "d;float argument required", &x))
return -1; return -1;
if (prec < 0) if (prec < 0)
prec = 6; prec = 6;
if (prec > 50)
prec = 50; /* Arbitrary limitation */
if (type == 'f' && fabs(x)/1e25 >= 1e25) if (type == 'f' && fabs(x)/1e25 >= 1e25)
type = 'g'; type = 'g';
sprintf(fmt, "%%%s.%d%c", (flags&F_ALT) ? "#" : "", prec, type); sprintf(fmt, "%%%s.%d%c", (flags&F_ALT) ? "#" : "", prec, type);
/* worst case length calc to ensure no buffer overrun:
fmt = %#.<prec>g
buf = '-' + [0-9]*prec + '.' + 'e+' + (longest exp
for any double rep.)
len = 1 + prec + 1 + 2 + 5 = 9 + prec
If prec=0 the effective precision is 1 (the leading digit is
always given), therefore increase by one to 10+prec. */
if (buflen <= (size_t)10 + (size_t)prec) {
PyErr_SetString(PyExc_OverflowError,
"formatted float is too long (precision too long?)");
return -1;
}
sprintf(buf, fmt, x); sprintf(buf, fmt, x);
return strlen(buf); return strlen(buf);
} }
static int static int
formatint(buf, flags, prec, type, v) formatint(buf, buflen, flags, prec, type, v)
char *buf; char *buf;
size_t buflen;
int flags; int flags;
int prec; int prec;
int type; int type;
PyObject *v; PyObject *v;
{ {
/* fmt = '%#.' + `prec` + 'l' + `type`
worst case length = 3 + 10 (len of INT_MAX) + 1 + 1 = 15 (use 20)*/
char fmt[20]; char fmt[20];
long x; long x;
if (!PyArg_Parse(v, "l;int argument required", &x)) if (!PyArg_Parse(v, "l;int argument required", &x))
@ -2372,15 +2397,24 @@ formatint(buf, flags, prec, type, v)
if (prec < 0) if (prec < 0)
prec = 1; prec = 1;
sprintf(fmt, "%%%s.%dl%c", (flags&F_ALT) ? "#" : "", prec, type); sprintf(fmt, "%%%s.%dl%c", (flags&F_ALT) ? "#" : "", prec, type);
/* buf = '+'/'-'/'0'/'0x' + '[0-9]'*max(prec,len(x in octal))
worst case buf = '0x' + [0-9]*prec, where prec >= 11 */
if (buflen <= 13 || buflen <= (size_t)2+(size_t)prec) {
PyErr_SetString(PyExc_OverflowError,
"formatted integer is too long (precision too long?)");
return -1;
}
sprintf(buf, fmt, x); sprintf(buf, fmt, x);
return strlen(buf); return strlen(buf);
} }
static int static int
formatchar(buf, v) formatchar(buf, buflen, v)
char *buf; char *buf;
size_t buflen;
PyObject *v; PyObject *v;
{ {
/* presume that the buffer is at least 2 characters long */
if (PyString_Check(v)) { if (PyString_Check(v)) {
if (!PyArg_Parse(v, "c;%c requires int or char", &buf[0])) if (!PyArg_Parse(v, "c;%c requires int or char", &buf[0]))
return -1; return -1;
@ -2394,7 +2428,15 @@ formatchar(buf, v)
} }
/* fmt%(v1,v2,...) is roughly equivalent to sprintf(fmt, v1, v2, ...) */ /* fmt%(v1,v2,...) is roughly equivalent to sprintf(fmt, v1, v2, ...)
FORMATBUFLEN is the length of the buffer in which the floats, ints, &
chars are formatted. XXX This is a magic number. Each formatting
routine does bounds checking to ensure no overflow, but a better
solution may be to malloc a buffer of appropriate size for each
format. For now, the current solution is sufficient.
*/
#define FORMATBUFLEN (size_t)120
PyObject * PyObject *
PyString_Format(format, args) PyString_Format(format, args)
@ -2451,10 +2493,10 @@ PyString_Format(format, args)
int fill; int fill;
PyObject *v = NULL; PyObject *v = NULL;
PyObject *temp = NULL; PyObject *temp = NULL;
char *buf; char *pbuf;
int sign; int sign;
int len; int len;
char tmpbuf[120]; /* For format{float,int,char}() */ char formatbuf[FORMATBUFLEN]; /* For format{float,int,char}() */
char *fmt_start = fmt; char *fmt_start = fmt;
fmt++; fmt++;
@ -2602,7 +2644,7 @@ PyString_Format(format, args)
fill = ' '; fill = ' ';
switch (c) { switch (c) {
case '%': case '%':
buf = "%"; pbuf = "%";
len = 1; len = 1;
break; break;
case 's': case 's':
@ -2622,7 +2664,7 @@ PyString_Format(format, args)
"%s argument has non-string str()"); "%s argument has non-string str()");
goto error; goto error;
} }
buf = PyString_AsString(temp); pbuf = PyString_AsString(temp);
len = PyString_Size(temp); len = PyString_Size(temp);
if (prec >= 0 && len > prec) if (prec >= 0 && len > prec)
len = prec; len = prec;
@ -2635,8 +2677,8 @@ PyString_Format(format, args)
case 'X': case 'X':
if (c == 'i') if (c == 'i')
c = 'd'; c = 'd';
buf = tmpbuf; pbuf = formatbuf;
len = formatint(buf, flags, prec, c, v); len = formatint(pbuf, sizeof(formatbuf), flags, prec, c, v);
if (len < 0) if (len < 0)
goto error; goto error;
sign = (c == 'd'); sign = (c == 'd');
@ -2644,9 +2686,9 @@ PyString_Format(format, args)
fill = '0'; fill = '0';
if ((flags&F_ALT) && if ((flags&F_ALT) &&
(c == 'x' || c == 'X') && (c == 'x' || c == 'X') &&
buf[0] == '0' && buf[1] == c) { pbuf[0] == '0' && pbuf[1] == c) {
*res++ = *buf++; *res++ = *pbuf++;
*res++ = *buf++; *res++ = *pbuf++;
rescnt -= 2; rescnt -= 2;
len -= 2; len -= 2;
width -= 2; width -= 2;
@ -2660,8 +2702,8 @@ PyString_Format(format, args)
case 'f': case 'f':
case 'g': case 'g':
case 'G': case 'G':
buf = tmpbuf; pbuf = formatbuf;
len = formatfloat(buf, flags, prec, c, v); len = formatfloat(pbuf, sizeof(formatbuf), flags, prec, c, v);
if (len < 0) if (len < 0)
goto error; goto error;
sign = 1; sign = 1;
@ -2669,8 +2711,8 @@ PyString_Format(format, args)
fill = '0'; fill = '0';
break; break;
case 'c': case 'c':
buf = tmpbuf; pbuf = formatbuf;
len = formatchar(buf, v); len = formatchar(pbuf, sizeof(formatbuf), v);
if (len < 0) if (len < 0)
goto error; goto error;
break; break;
@ -2681,8 +2723,8 @@ PyString_Format(format, args)
goto error; goto error;
} }
if (sign) { if (sign) {
if (*buf == '-' || *buf == '+') { if (*pbuf == '-' || *pbuf == '+') {
sign = *buf++; sign = *pbuf++;
len--; len--;
} }
else if (flags & F_SIGN) else if (flags & F_SIGN)
@ -2718,7 +2760,7 @@ PyString_Format(format, args)
} }
if (sign && fill == ' ') if (sign && fill == ' ')
*res++ = sign; *res++ = sign;
memcpy(res, buf, len); memcpy(res, pbuf, len);
res += len; res += len;
rescnt -= len; rescnt -= len;
while (--width >= len) { while (--width >= len) {

View File

@ -66,7 +66,7 @@ Unicode Integration Proposal (see file Misc/unicode.txt).
#include "mymath.h" #include "mymath.h"
#include "unicodeobject.h" #include "unicodeobject.h"
#include "ucnhash.h" #include <ucnhash.h>
#if defined(HAVE_LIMITS_H) #if defined(HAVE_LIMITS_H)
#include <limits.h> #include <limits.h>
@ -1244,14 +1244,14 @@ PyObject *PyUnicode_DecodeUnicodeEscape(const char *s,
goto onError; goto onError;
ucnFallthrough: ucnFallthrough:
/* fall through on purpose */ /* fall through on purpose */
default: default:
*p++ = '\\'; *p++ = '\\';
*p++ = (unsigned char)s[-1]; *p++ = (unsigned char)s[-1];
break; break;
} }
} }
if (_PyUnicode_Resize(v, (int)(p - buf))) if (_PyUnicode_Resize(v, (int)(p - buf)))
goto onError; goto onError;
return (PyObject *)v; return (PyObject *)v;
onError: onError:
@ -4373,11 +4373,14 @@ int usprintf(va_alist) va_dcl
static int static int
formatfloat(Py_UNICODE *buf, formatfloat(Py_UNICODE *buf,
size_t buflen,
int flags, int flags,
int prec, int prec,
int type, int type,
PyObject *v) PyObject *v)
{ {
/* fmt = '%#.' + `prec` + `type`
worst case length = 3 + 10 (len of INT_MAX) + 1 = 14 (use 20)*/
char fmt[20]; char fmt[20];
double x; double x;
@ -4386,21 +4389,34 @@ formatfloat(Py_UNICODE *buf,
return -1; return -1;
if (prec < 0) if (prec < 0)
prec = 6; prec = 6;
if (prec > 50)
prec = 50; /* Arbitrary limitation */
if (type == 'f' && (fabs(x) / 1e25) >= 1e25) if (type == 'f' && (fabs(x) / 1e25) >= 1e25)
type = 'g'; type = 'g';
sprintf(fmt, "%%%s.%d%c", (flags & F_ALT) ? "#" : "", prec, type); sprintf(fmt, "%%%s.%d%c", (flags & F_ALT) ? "#" : "", prec, type);
/* worst case length calc to ensure no buffer overrun:
fmt = %#.<prec>g
buf = '-' + [0-9]*prec + '.' + 'e+' + (longest exp
for any double rep.)
len = 1 + prec + 1 + 2 + 5 = 9 + prec
If prec=0 the effective precision is 1 (the leading digit is
always given), therefore increase by one to 10+prec. */
if (buflen <= (size_t)10 + (size_t)prec) {
PyErr_SetString(PyExc_OverflowError,
"formatted float is too long (precision too long?)");
return -1;
}
return usprintf(buf, fmt, x); return usprintf(buf, fmt, x);
} }
static int static int
formatint(Py_UNICODE *buf, formatint(Py_UNICODE *buf,
size_t buflen,
int flags, int flags,
int prec, int prec,
int type, int type,
PyObject *v) PyObject *v)
{ {
/* fmt = '%#.' + `prec` + 'l' + `type`
worst case length = 3 + 10 (len of INT_MAX) + 1 + 1 = 15 (use 20)*/
char fmt[20]; char fmt[20];
long x; long x;
@ -4409,14 +4425,23 @@ formatint(Py_UNICODE *buf,
return -1; return -1;
if (prec < 0) if (prec < 0)
prec = 1; prec = 1;
/* buf = '+'/'-'/'0'/'0x' + '[0-9]'*max(prec,len(x in octal))
worst case buf = '0x' + [0-9]*prec, where prec >= 11 */
if (buflen <= 13 || buflen <= (size_t)2+(size_t)prec) {
PyErr_SetString(PyExc_OverflowError,
"formatted integer is too long (precision too long?)");
return -1;
}
sprintf(fmt, "%%%s.%dl%c", (flags & F_ALT) ? "#" : "", prec, type); sprintf(fmt, "%%%s.%dl%c", (flags & F_ALT) ? "#" : "", prec, type);
return usprintf(buf, fmt, x); return usprintf(buf, fmt, x);
} }
static int static int
formatchar(Py_UNICODE *buf, formatchar(Py_UNICODE *buf,
PyObject *v) size_t buflen,
PyObject *v)
{ {
/* presume that the buffer is at least 2 characters long */
if (PyUnicode_Check(v)) { if (PyUnicode_Check(v)) {
if (PyUnicode_GET_SIZE(v) != 1) if (PyUnicode_GET_SIZE(v) != 1)
goto onError; goto onError;
@ -4446,6 +4471,16 @@ formatchar(Py_UNICODE *buf,
return -1; return -1;
} }
/* fmt%(v1,v2,...) is roughly equivalent to sprintf(fmt, v1, v2, ...)
FORMATBUFLEN is the length of the buffer in which the floats, ints, &
chars are formatted. XXX This is a magic number. Each formatting
routine does bounds checking to ensure no overflow, but a better
solution may be to malloc a buffer of appropriate size for each
format. For now, the current solution is sufficient.
*/
#define FORMATBUFLEN (size_t)120
PyObject *PyUnicode_Format(PyObject *format, PyObject *PyUnicode_Format(PyObject *format,
PyObject *args) PyObject *args)
{ {
@ -4505,10 +4540,10 @@ PyObject *PyUnicode_Format(PyObject *format,
Py_UNICODE fill; Py_UNICODE fill;
PyObject *v = NULL; PyObject *v = NULL;
PyObject *temp = NULL; PyObject *temp = NULL;
Py_UNICODE *buf; Py_UNICODE *pbuf;
Py_UNICODE sign; Py_UNICODE sign;
int len; int len;
Py_UNICODE tmpbuf[120]; /* For format{float,int,char}() */ Py_UNICODE formatbuf[FORMATBUFLEN]; /* For format{float,int,char}() */
fmt++; fmt++;
if (*fmt == '(') { if (*fmt == '(') {
@ -4658,8 +4693,9 @@ PyObject *PyUnicode_Format(PyObject *format,
switch (c) { switch (c) {
case '%': case '%':
buf = tmpbuf; pbuf = formatbuf;
buf[0] = '%'; /* presume that buffer length is at least 1 */
pbuf[0] = '%';
len = 1; len = 1;
break; break;
@ -4695,7 +4731,7 @@ PyObject *PyUnicode_Format(PyObject *format,
if (temp == NULL) if (temp == NULL)
goto onError; goto onError;
} }
buf = PyUnicode_AS_UNICODE(temp); pbuf = PyUnicode_AS_UNICODE(temp);
len = PyUnicode_GET_SIZE(temp); len = PyUnicode_GET_SIZE(temp);
if (prec >= 0 && len > prec) if (prec >= 0 && len > prec)
len = prec; len = prec;
@ -4709,8 +4745,9 @@ PyObject *PyUnicode_Format(PyObject *format,
case 'X': case 'X':
if (c == 'i') if (c == 'i')
c = 'd'; c = 'd';
buf = tmpbuf; pbuf = formatbuf;
len = formatint(buf, flags, prec, c, v); len = formatint(pbuf, sizeof(formatbuf)/sizeof(Py_UNICODE),
flags, prec, c, v);
if (len < 0) if (len < 0)
goto onError; goto onError;
sign = (c == 'd'); sign = (c == 'd');
@ -4718,9 +4755,9 @@ PyObject *PyUnicode_Format(PyObject *format,
fill = '0'; fill = '0';
if ((flags&F_ALT) && if ((flags&F_ALT) &&
(c == 'x' || c == 'X') && (c == 'x' || c == 'X') &&
buf[0] == '0' && buf[1] == c) { pbuf[0] == '0' && pbuf[1] == c) {
*res++ = *buf++; *res++ = *pbuf++;
*res++ = *buf++; *res++ = *pbuf++;
rescnt -= 2; rescnt -= 2;
len -= 2; len -= 2;
width -= 2; width -= 2;
@ -4735,8 +4772,9 @@ PyObject *PyUnicode_Format(PyObject *format,
case 'f': case 'f':
case 'g': case 'g':
case 'G': case 'G':
buf = tmpbuf; pbuf = formatbuf;
len = formatfloat(buf, flags, prec, c, v); len = formatfloat(pbuf, sizeof(formatbuf)/sizeof(Py_UNICODE),
flags, prec, c, v);
if (len < 0) if (len < 0)
goto onError; goto onError;
sign = 1; sign = 1;
@ -4745,8 +4783,8 @@ PyObject *PyUnicode_Format(PyObject *format,
break; break;
case 'c': case 'c':
buf = tmpbuf; pbuf = formatbuf;
len = formatchar(buf, v); len = formatchar(pbuf, sizeof(formatbuf)/sizeof(Py_UNICODE), v);
if (len < 0) if (len < 0)
goto onError; goto onError;
break; break;
@ -4758,8 +4796,8 @@ PyObject *PyUnicode_Format(PyObject *format,
goto onError; goto onError;
} }
if (sign) { if (sign) {
if (*buf == '-' || *buf == '+') { if (*pbuf == '-' || *pbuf == '+') {
sign = *buf++; sign = *pbuf++;
len--; len--;
} }
else if (flags & F_SIGN) else if (flags & F_SIGN)
@ -4795,7 +4833,7 @@ PyObject *PyUnicode_Format(PyObject *format,
} }
if (sign && fill == ' ') if (sign && fill == ' ')
*res++ = sign; *res++ = sign;
memcpy(res, buf, len * sizeof(Py_UNICODE)); memcpy(res, pbuf, len * sizeof(Py_UNICODE));
res += len; res += len;
rescnt -= len; rescnt -= len;
while (--width >= len) { while (--width >= len) {