summaryrefslogtreecommitdiff
path: root/lang
diff options
context:
space:
mode:
authorMartin Wilke <miwi@FreeBSD.org>2008-09-11 08:05:23 +0000
committerMartin Wilke <miwi@FreeBSD.org>2008-09-11 08:05:23 +0000
commit5d6556dc39098800e88d6345d23bd810c72d0b39 (patch)
treee561cacf743e1c5aab98ca14014ecdcbdc988693 /lang
parent- Document python -- multiple vulnerabilities (diff)
- add patches from upstream svn rev.65333, fix integer overflows in
memory allocation (CVE-2008-2315 and CVE-2008-2316) - also apply upstream svn rev.65262, fixes overflow checks in memory allocation (CVE-2008-3142 and CVE-2008-3144) Approved by: portmgr (pav) Security: http://www.vuxml.org/freebsd/0dccaa28-7f3c-11dd-8de5-0030843d3802.html
Notes
Notes: svn path=/head/; revision=220331
Diffstat (limited to 'lang')
-rw-r--r--lang/python24/Makefile2
-rw-r--r--lang/python24/files/patch-include-pymem.h58
-rw-r--r--lang/python24/files/patch-module_arraymodule.c33
-rw-r--r--lang/python24/files/patch-module_obmalloc.c34
-rw-r--r--lang/python24/files/patch-module_selectmodule.c16
-rw-r--r--lang/python24/files/patch-modules_almodule.c14
-rw-r--r--lang/python24/files/patch-modules_gcmodule.c23
-rw-r--r--lang/python24/files/patch-modules_mmapmodule.c11
-rw-r--r--lang/python24/files/patch-modules_stropmodule.c31
-rw-r--r--lang/python24/files/patch-objects-bufferobject.c13
-rw-r--r--lang/python24/files/patch-objects_stringobject.c42
-rw-r--r--lang/python24/files/patch-objects_tupleobject.c17
-rw-r--r--lang/python24/files/patch-objects_unicodeobject.c112
-rw-r--r--lang/python24/files/patch-python_mysnprintf.c55
14 files changed, 460 insertions, 1 deletions
diff --git a/lang/python24/Makefile b/lang/python24/Makefile
index 3e719978b300..ca99497844b4 100644
--- a/lang/python24/Makefile
+++ b/lang/python24/Makefile
@@ -7,7 +7,7 @@
PORTNAME= python24
PORTVERSION= 2.4.5
-PORTREVISION= 1
+PORTREVISION= 2
CATEGORIES= lang python ipv6
MASTER_SITES= ${PYTHON_MASTER_SITES}
MASTER_SITE_SUBDIR= ${PYTHON_MASTER_SITE_SUBDIR}
diff --git a/lang/python24/files/patch-include-pymem.h b/lang/python24/files/patch-include-pymem.h
new file mode 100644
index 000000000000..cafb6ae52698
--- /dev/null
+++ b/lang/python24/files/patch-include-pymem.h
@@ -0,0 +1,58 @@
+--- Include/pymem.h.orig 2008-03-02 20:20:32.000000000 +0100
++++ Include/pymem.h
+@@ -66,8 +66,12 @@ PyAPI_FUNC(void) PyMem_Free(void *);
+ for malloc(0), which would be treated as an error. Some platforms
+ would return a pointer with no memory behind it, which would break
+ pymalloc. To solve these problems, allocate an extra byte. */
+-#define PyMem_MALLOC(n) malloc((n) ? (n) : 1)
+-#define PyMem_REALLOC(p, n) realloc((p), (n) ? (n) : 1)
++/* Returns NULL to indicate error if a negative size or size larger than
++ Py_ssize_t can represent is supplied. Helps prevents security holes. */
++#define PyMem_MALLOC(n) (((n) < 0 || (n) > INT_MAX) ? NULL \
++ : malloc((n) ? (n) : 1))
++#define PyMem_REALLOC(p, n) (((n) < 0 || (n) > INT_MAX) ? NULL \
++ : realloc((p), (n) ? (n) : 1))
+
+ #endif /* PYMALLOC_DEBUG */
+
+@@ -80,24 +84,31 @@ PyAPI_FUNC(void) PyMem_Free(void *);
+ * Type-oriented memory interface
+ * ==============================
+ *
+- * These are carried along for historical reasons. There's rarely a good
+- * reason to use them anymore (you can just as easily do the multiply and
+- * cast yourself).
++ * Allocate memory for n objects of the given type. Returns a new pointer
++ * or NULL if the request was too large or memory allocation failed. Use
++ * these macros rather than doing the multiplication yourself so that proper
++ * overflow checking is always done.
+ */
+
+ #define PyMem_New(type, n) \
+- ( assert((n) <= PY_SIZE_MAX / sizeof(type)) , \
++ ( ((n) > INT_MAX / sizeof(type)) ? NULL : \
+ ( (type *) PyMem_Malloc((n) * sizeof(type)) ) )
+ #define PyMem_NEW(type, n) \
+- ( assert((n) <= PY_SIZE_MAX / sizeof(type)) , \
++ ( ((n) > INT_MAX / sizeof(type)) ? NULL : \
+ ( (type *) PyMem_MALLOC((n) * sizeof(type)) ) )
+
++/*
++ * The value of (p) is always clobbered by this macro regardless of success.
++ * The caller MUST check if (p) is NULL afterwards and deal with the memory
++ * error if so. This means the original value of (p) MUST be saved for the
++ * caller's memory error handler to not lose track of it.
++ */
+ #define PyMem_Resize(p, type, n) \
+- ( assert((n) <= PY_SIZE_MAX / sizeof(type)) , \
+- ( (p) = (type *) PyMem_Realloc((p), (n) * sizeof(type)) ) )
++ ( (p) = ((n) > INT_MAX / sizeof(type)) ? NULL : \
++ (type *) PyMem_Realloc((p), (n) * sizeof(type)) )
+ #define PyMem_RESIZE(p, type, n) \
+- ( assert((n) <= PY_SIZE_MAX / sizeof(type)) , \
+- ( (p) = (type *) PyMem_REALLOC((p), (n) * sizeof(type)) ) )
++ ( (p) = ((n) > INT_MAX / sizeof(type)) ? NULL : \
++ (type *) PyMem_REALLOC((p), (n) * sizeof(type)) )
+
+ /* In order to avoid breaking old code mixing PyObject_{New, NEW} with
+ PyMem_{Del, DEL} and PyMem_{Free, FREE}, the PyMem "release memory"
diff --git a/lang/python24/files/patch-module_arraymodule.c b/lang/python24/files/patch-module_arraymodule.c
new file mode 100644
index 000000000000..6dcfb467f619
--- /dev/null
+++ b/lang/python24/files/patch-module_arraymodule.c
@@ -0,0 +1,33 @@
+--- Modules/arraymodule.c.orig 2008-03-02 20:20:32.000000000 +0100
++++ Modules/arraymodule.c
+@@ -814,6 +814,7 @@ static int
+ array_do_extend(arrayobject *self, PyObject *bb)
+ {
+ int size;
++ char *old_item;
+
+ if (!array_Check(bb))
+ return array_iter_extend(self, bb);
+@@ -829,10 +830,11 @@ array_do_extend(arrayobject *self, PyObj
+ return -1;
+ }
+ size = self->ob_size + b->ob_size;
++ old_item = self->ob_item;
+ PyMem_RESIZE(self->ob_item, char, size*self->ob_descr->itemsize);
+ if (self->ob_item == NULL) {
+- PyObject_Del(self);
+- PyErr_NoMemory();
++ self->ob_item = old_item;
++ PyErr_NoMemory();
+ return -1;
+ }
+ memcpy(self->ob_item + self->ob_size*self->ob_descr->itemsize,
+@@ -884,7 +886,7 @@ array_inplace_repeat(arrayobject *self,
+ if (size > INT_MAX / n) {
+ return PyErr_NoMemory();
+ }
+- PyMem_Resize(items, char, n * size);
++ PyMem_RESIZE(items, char, n * size);
+ if (items == NULL)
+ return PyErr_NoMemory();
+ p = items;
diff --git a/lang/python24/files/patch-module_obmalloc.c b/lang/python24/files/patch-module_obmalloc.c
new file mode 100644
index 000000000000..9373b40be181
--- /dev/null
+++ b/lang/python24/files/patch-module_obmalloc.c
@@ -0,0 +1,34 @@
+--- Objects/obmalloc.c.orig 2005-07-11 07:57:11.000000000 +0200
++++ Objects/obmalloc.c
+@@ -585,6 +585,15 @@ PyObject_Malloc(size_t nbytes)
+ uint size;
+
+ /*
++ * Limit ourselves to INT_MAX bytes to prevent security holes.
++ * Most python internals blindly use a signed Py_ssize_t to track
++ * things without checking for overflows or negatives.
++ * As size_t is unsigned, checking for nbytes < 0 is not required.
++ */
++ if (nbytes > INT_MAX)
++ return NULL;
++
++ /*
+ * This implicitly redirects malloc(0).
+ */
+ if ((nbytes - 1) < SMALL_REQUEST_THRESHOLD) {
+@@ -814,6 +823,15 @@ PyObject_Realloc(void *p, size_t nbytes)
+ if (p == NULL)
+ return PyObject_Malloc(nbytes);
+
++ /*
++ * Limit ourselves to INT_MAX bytes to prevent security holes.
++ * Most python internals blindly use a signed Py_ssize_t to track
++ * things without checking for overflows or negatives.
++ * As size_t is unsigned, checking for nbytes < 0 is not required.
++ */
++ if (nbytes > INT_MAX)
++ return NULL;
++
+ pool = POOL_ADDR(p);
+ if (Py_ADDRESS_IN_RANGE(p, pool)) {
+ /* We're in charge of this block */
diff --git a/lang/python24/files/patch-module_selectmodule.c b/lang/python24/files/patch-module_selectmodule.c
new file mode 100644
index 000000000000..bbcaab9b040d
--- /dev/null
+++ b/lang/python24/files/patch-module_selectmodule.c
@@ -0,0 +1,16 @@
+--- Modules/selectmodule.c.orig 2006-09-27 21:17:32.000000000 +0200
++++ Modules/selectmodule.c
+@@ -342,10 +342,12 @@ update_ufd_array(pollObject *self)
+ {
+ int i, pos;
+ PyObject *key, *value;
++ struct pollfd *old_ufds = self->ufds;
+
+ self->ufd_len = PyDict_Size(self->dict);
+- PyMem_Resize(self->ufds, struct pollfd, self->ufd_len);
++ PyMem_RESIZE(self->ufds, struct pollfd, self->ufd_len);
+ if (self->ufds == NULL) {
++ self->ufds = old_ufds;
+ PyErr_NoMemory();
+ return 0;
+ }
diff --git a/lang/python24/files/patch-modules_almodule.c b/lang/python24/files/patch-modules_almodule.c
new file mode 100644
index 000000000000..dbd031e28b8f
--- /dev/null
+++ b/lang/python24/files/patch-modules_almodule.c
@@ -0,0 +1,14 @@
+--- Modules/almodule.c.orig 2006-09-27 21:17:32.000000000 +0200
++++ Modules/almodule.c
+@@ -1633,9 +1633,11 @@ al_QueryValues(PyObject *self, PyObject
+ if (nvals < 0)
+ goto cleanup;
+ if (nvals > setsize) {
++ ALvalue *old_return_set = return_set;
+ setsize = nvals;
+ PyMem_RESIZE(return_set, ALvalue, setsize);
+ if (return_set == NULL) {
++ return_set = old_return_set;
+ PyErr_NoMemory();
+ goto cleanup;
+ }
diff --git a/lang/python24/files/patch-modules_gcmodule.c b/lang/python24/files/patch-modules_gcmodule.c
new file mode 100644
index 000000000000..6f16ac7ee2e0
--- /dev/null
+++ b/lang/python24/files/patch-modules_gcmodule.c
@@ -0,0 +1,23 @@
+--- Modules/gcmodule.c.orig 2006-09-28 19:08:01.000000000 +0200
++++ Modules/gcmodule.c
+@@ -1249,7 +1249,10 @@ PyObject *
+ _PyObject_GC_Malloc(size_t basicsize)
+ {
+ PyObject *op;
+- PyGC_Head *g = PyObject_MALLOC(sizeof(PyGC_Head) + basicsize);
++ PyGC_Head *g;
++ if (basicsize > INT_MAX - sizeof(PyGC_Head))
++ return PyErr_NoMemory();
++ g = PyObject_MALLOC(sizeof(PyGC_Head) + basicsize);
+ if (g == NULL)
+ return PyErr_NoMemory();
+ g->gc.gc_refs = GC_UNTRACKED;
+@@ -1291,6 +1294,8 @@ _PyObject_GC_Resize(PyVarObject *op, int
+ {
+ const size_t basicsize = _PyObject_VAR_SIZE(op->ob_type, nitems);
+ PyGC_Head *g = AS_GC(op);
++ if (basicsize > INT_MAX - sizeof(PyGC_Head))
++ return (PyVarObject *)PyErr_NoMemory();
+ g = PyObject_REALLOC(g, sizeof(PyGC_Head) + basicsize);
+ if (g == NULL)
+ return (PyVarObject *)PyErr_NoMemory();
diff --git a/lang/python24/files/patch-modules_mmapmodule.c b/lang/python24/files/patch-modules_mmapmodule.c
new file mode 100644
index 000000000000..0c2f7d761971
--- /dev/null
+++ b/lang/python24/files/patch-modules_mmapmodule.c
@@ -0,0 +1,11 @@
+--- Modules/mmapmodule.c.orig 2008-08-05 12:00:52.000000000 +0200
++++ Modules/mmapmodule.c
+@@ -223,7 +223,7 @@ mmap_read_method(mmap_object *self,
+ return(NULL);
+
+ /* silently 'adjust' out-of-range requests */
+- if ((self->pos + num_bytes) > self->size) {
++ if (num_bytes > self->size - self->pos) {
+ num_bytes -= (self->pos+num_bytes) - self->size;
+ }
+ result = Py_BuildValue("s#", self->data+self->pos, num_bytes);
diff --git a/lang/python24/files/patch-modules_stropmodule.c b/lang/python24/files/patch-modules_stropmodule.c
new file mode 100644
index 000000000000..13e0c837b3c6
--- /dev/null
+++ b/lang/python24/files/patch-modules_stropmodule.c
@@ -0,0 +1,31 @@
+--- Modules/stropmodule.c.orig 2008-03-02 20:20:32.000000000 +0100
++++ Modules/stropmodule.c
+@@ -214,6 +214,13 @@ strop_joinfields(PyObject *self, PyObjec
+ return NULL;
+ }
+ slen = PyString_GET_SIZE(item);
++ if (slen > INT_MAX - reslen ||
++ seplen > INT_MAX - reslen - seplen) {
++ PyErr_SetString(PyExc_OverflowError,
++ "input too long");
++ Py_DECREF(res);
++ return NULL;
++ }
+ while (reslen + slen + seplen >= sz) {
+ if (_PyString_Resize(&res, sz * 2) < 0)
+ return NULL;
+@@ -251,6 +258,14 @@ strop_joinfields(PyObject *self, PyObjec
+ return NULL;
+ }
+ slen = PyString_GET_SIZE(item);
++ if (slen > INT_MAX - reslen ||
++ seplen > INT_MAX - reslen - seplen) {
++ PyErr_SetString(PyExc_OverflowError,
++ "input too long");
++ Py_DECREF(res);
++ Py_XDECREF(item);
++ return NULL;
++ }
+ while (reslen + slen + seplen >= sz) {
+ if (_PyString_Resize(&res, sz * 2) < 0) {
+ Py_DECREF(item);
diff --git a/lang/python24/files/patch-objects-bufferobject.c b/lang/python24/files/patch-objects-bufferobject.c
new file mode 100644
index 000000000000..6d750015d846
--- /dev/null
+++ b/lang/python24/files/patch-objects-bufferobject.c
@@ -0,0 +1,13 @@
+--- Objects/bufferobject.c.orig 2008-03-02 20:20:32.000000000 +0100
++++ Objects/bufferobject.c
+@@ -384,6 +384,10 @@ buffer_repeat(PyBufferObject *self, int
+ count = 0;
+ if (!get_buf(self, &ptr, &size))
+ return NULL;
++ if (count > INT_MAX / size) {
++ PyErr_SetString(PyExc_MemoryError, "result too large");
++ return NULL;
++ }
+ ob = PyString_FromStringAndSize(NULL, size * count);
+ if ( ob == NULL )
+ return NULL;
diff --git a/lang/python24/files/patch-objects_stringobject.c b/lang/python24/files/patch-objects_stringobject.c
new file mode 100644
index 000000000000..0dd33f7a5cc0
--- /dev/null
+++ b/lang/python24/files/patch-objects_stringobject.c
@@ -0,0 +1,42 @@
+--- Objects/stringobject.c.orig 2006-10-06 21:26:14.000000000 +0200
++++ Objects/stringobject.c
+@@ -69,6 +69,11 @@ PyString_FromStringAndSize(const char *s
+ return (PyObject *)op;
+ }
+
++ if (size > INT_MAX - sizeof(PyStringObject)) {
++ PyErr_SetString(PyExc_OverflowError, "string is too large");
++ return NULL;
++ }
++
+ /* Inline PyObject_NewVar */
+ op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size);
+ if (op == NULL)
+@@ -104,7 +109,7 @@ PyString_FromString(const char *str)
+
+ assert(str != NULL);
+ size = strlen(str);
+- if (size > INT_MAX) {
++ if (size > INT_MAX - sizeof(PyStringObject)) {
+ PyErr_SetString(PyExc_OverflowError,
+ "string is too long for a Python string");
+ return NULL;
+@@ -907,7 +912,18 @@ string_concat(register PyStringObject *a
+ Py_INCREF(a);
+ return (PyObject *)a;
+ }
++ /* Check that string sizes are not negative, to prevent an
++ overflow in cases where we are passed incorrectly-created
++ strings with negative lengths (due to a bug in other code).
++ */
+ size = a->ob_size + b->ob_size;
++ if (a->ob_size < 0 || b->ob_size < 0 ||
++ a->ob_size > INT_MAX - b->ob_size) {
++ PyErr_SetString(PyExc_OverflowError,
++ "strings are too large to concat");
++ return NULL;
++ }
++
+ /* Inline PyObject_NewVar */
+ op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size);
+ if (op == NULL)
diff --git a/lang/python24/files/patch-objects_tupleobject.c b/lang/python24/files/patch-objects_tupleobject.c
new file mode 100644
index 000000000000..9d53d3f265a4
--- /dev/null
+++ b/lang/python24/files/patch-objects_tupleobject.c
@@ -0,0 +1,17 @@
+--- Objects/tupleobject.c.orig 2006-03-17 20:04:15.000000000 +0100
++++ Objects/tupleobject.c
+@@ -60,11 +60,12 @@ PyTuple_New(register int size)
+ int nbytes = size * sizeof(PyObject *);
+ /* Check for overflow */
+ if (nbytes / sizeof(PyObject *) != (size_t)size ||
+- (nbytes += sizeof(PyTupleObject) - sizeof(PyObject *))
+- <= 0)
++ (nbytes > INT_MAX - sizeof(PyTupleObject) - sizeof(PyObject *)))
+ {
+ return PyErr_NoMemory();
+ }
++ nbytes += sizeof(PyTupleObject) - sizeof(PyObject *);
++
+ op = PyObject_GC_NewVar(PyTupleObject, &PyTuple_Type, size);
+ if (op == NULL)
+ return NULL;
diff --git a/lang/python24/files/patch-objects_unicodeobject.c b/lang/python24/files/patch-objects_unicodeobject.c
new file mode 100644
index 000000000000..55e0fc76d438
--- /dev/null
+++ b/lang/python24/files/patch-objects_unicodeobject.c
@@ -0,0 +1,112 @@
+--- Objects/unicodeobject.c.orig 2006-10-05 20:08:58.000000000 +0200
++++ Objects/unicodeobject.c
+@@ -186,6 +186,11 @@ PyUnicodeObject *_PyUnicode_New(int leng
+ return unicode_empty;
+ }
+
++ /* Ensure we won't overflow the size. */
++ if (length > ((INT_MAX / sizeof(Py_UNICODE)) - 1)) {
++ return (PyUnicodeObject *)PyErr_NoMemory();
++ }
++
+ /* Unicode freelist & memory allocation */
+ if (unicode_freelist) {
+ unicode = unicode_freelist;
+@@ -1040,6 +1045,9 @@ PyObject *PyUnicode_EncodeUTF7(const Py_
+ char * out;
+ char * start;
+
++ if (cbAllocated / 5 != size)
++ return PyErr_NoMemory();
++
+ if (size == 0)
+ return PyString_FromStringAndSize(NULL, 0);
+
+@@ -1638,6 +1646,7 @@ PyUnicode_EncodeUTF16(const Py_UNICODE *
+ {
+ PyObject *v;
+ unsigned char *p;
++ int nsize, bytesize;
+ #ifdef Py_UNICODE_WIDE
+ int i, pairs;
+ #else
+@@ -1662,8 +1671,15 @@ PyUnicode_EncodeUTF16(const Py_UNICODE *
+ if (s[i] >= 0x10000)
+ pairs++;
+ #endif
+- v = PyString_FromStringAndSize(NULL,
+- 2 * (size + pairs + (byteorder == 0)));
++ /* 2 * (size + pairs + (byteorder == 0)) */
++ if (size > INT_MAX ||
++ size > INT_MAX - pairs - (byteorder == 0))
++ return PyErr_NoMemory();
++ nsize = (size + pairs + (byteorder == 0));
++ bytesize = nsize * 2;
++ if (bytesize / 2 != nsize)
++ return PyErr_NoMemory();
++ v = PyString_FromStringAndSize(NULL, bytesize);
+ if (v == NULL)
+ return NULL;
+
+@@ -1977,6 +1993,11 @@ PyObject *unicodeescape_string(const Py_
+ char *p;
+
+ static const char *hexdigit = "0123456789abcdef";
++#ifdef Py_UNICODE_WIDE
++ const int expandsize = 10;
++#else
++ const int expandsize = 6;
++#endif
+
+ /* Initial allocation is based on the longest-possible unichr
+ escape.
+@@ -1992,13 +2013,12 @@ PyObject *unicodeescape_string(const Py_
+ escape.
+ */
+
++ if (size > (INT_MAX - 2 - 1) / expandsize)
++ return PyErr_NoMemory();
++
+ repr = PyString_FromStringAndSize(NULL,
+ 2
+-#ifdef Py_UNICODE_WIDE
+- + 10*size
+-#else
+- + 6*size
+-#endif
++ + expandsize*size
+ + 1);
+ if (repr == NULL)
+ return NULL;
+@@ -2239,12 +2259,16 @@ PyObject *PyUnicode_EncodeRawUnicodeEsca
+ char *q;
+
+ static const char *hexdigit = "0123456789abcdef";
+-
+ #ifdef Py_UNICODE_WIDE
+- repr = PyString_FromStringAndSize(NULL, 10 * size);
++ const int expandsize = 10;
+ #else
+- repr = PyString_FromStringAndSize(NULL, 6 * size);
++ const int expandsize = 6;
+ #endif
++
++ if (size > INT_MAX / expandsize)
++ return PyErr_NoMemory();
++
++ repr = PyString_FromStringAndSize(NULL, expandsize * size);
+ if (repr == NULL)
+ return NULL;
+ if (size == 0)
+@@ -4289,6 +4313,11 @@ PyUnicodeObject *pad(PyUnicodeObject *se
+ return self;
+ }
+
++ if (left > INT_MAX - self->length ||
++ right > INT_MAX - (left + self->length)) {
++ PyErr_SetString(PyExc_OverflowError, "padded string is too long");
++ return NULL;
++ }
+ u = _PyUnicode_New(left + self->length + right);
+ if (u) {
+ if (left)
diff --git a/lang/python24/files/patch-python_mysnprintf.c b/lang/python24/files/patch-python_mysnprintf.c
new file mode 100644
index 000000000000..84b1d41f2b36
--- /dev/null
+++ b/lang/python24/files/patch-python_mysnprintf.c
@@ -0,0 +1,55 @@
+--- Python/mysnprintf.c.orig 2001-12-21 17:32:15.000000000 +0100
++++ Python/mysnprintf.c
+@@ -54,18 +54,28 @@ int
+ PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va)
+ {
+ int len; /* # bytes written, excluding \0 */
+-#ifndef HAVE_SNPRINTF
++#ifdef HAVE_SNPRINTF
++#define _PyOS_vsnprintf_EXTRA_SPACE 1
++#else
++#define _PyOS_vsnprintf_EXTRA_SPACE 512
+ char *buffer;
+ #endif
+ assert(str != NULL);
+ assert(size > 0);
+ assert(format != NULL);
++ /* We take a size_t as input but return an int. Sanity check
++ * our input so that it won't cause an overflow in the
++ * vsnprintf return value or the buffer malloc size. */
++ if (size > INT_MAX - _PyOS_vsnprintf_EXTRA_SPACE) {
++ len = -666;
++ goto Done;
++ }
+
+ #ifdef HAVE_SNPRINTF
+ len = vsnprintf(str, size, format, va);
+ #else
+ /* Emulate it. */
+- buffer = PyMem_MALLOC(size + 512);
++ buffer = PyMem_MALLOC(size + _PyOS_vsnprintf_EXTRA_SPACE);
+ if (buffer == NULL) {
+ len = -666;
+ goto Done;
+@@ -75,7 +85,7 @@ PyOS_vsnprintf(char *str, size_t size, c
+ if (len < 0)
+ /* ignore the error */;
+
+- else if ((size_t)len >= size + 512)
++ else if ((size_t)len >= size + _PyOS_vsnprintf_EXTRA_SPACE)
+ Py_FatalError("Buffer overflow in PyOS_snprintf/PyOS_vsnprintf");
+
+ else {
+@@ -86,8 +96,10 @@ PyOS_vsnprintf(char *str, size_t size, c
+ str[to_copy] = '\0';
+ }
+ PyMem_FREE(buffer);
+-Done:
+ #endif
+- str[size-1] = '\0';
++Done:
++ if (size > 0)
++ str[size-1] = '\0';
+ return len;
++#undef _PyOS_vsnprintf_EXTRA_SPACE
+ }