mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Patch #103373 from Donovan Baarda: This patch:
* fixes the zlib decompress sync flush bug as reported in bug #124981 * avoids repeat calls to (in|de)flateEnd when destroying (de)compression objects * raises exception when allocating unused_data fails * fixes memory leak when allocating unused_data fails * raises exception when allocating decompress data fails * removes vestigial code from decompress flush now that decompression returns all available data * tidies code so object compress/decompress/flush routines are consistent
This commit is contained in:
parent
bc8f72cccc
commit
9aff4a2ad0
1 changed files with 133 additions and 196 deletions
|
@ -381,6 +381,7 @@ Comp_dealloc(compobject *self)
|
||||||
static void
|
static void
|
||||||
Decomp_dealloc(compobject *self)
|
Decomp_dealloc(compobject *self)
|
||||||
{
|
{
|
||||||
|
if (self->is_initialised)
|
||||||
inflateEnd(&self->zst);
|
inflateEnd(&self->zst);
|
||||||
Py_XDECREF(self->unused_data);
|
Py_XDECREF(self->unused_data);
|
||||||
PyObject_Del(self);
|
PyObject_Del(self);
|
||||||
|
@ -397,28 +398,27 @@ static char comp_compress__doc__[] =
|
||||||
static PyObject *
|
static PyObject *
|
||||||
PyZlib_objcompress(compobject *self, PyObject *args)
|
PyZlib_objcompress(compobject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
int err = Z_OK, inplen;
|
int err, inplen, length = DEFAULTALLOC;
|
||||||
int length = DEFAULTALLOC;
|
|
||||||
PyObject *RetVal;
|
PyObject *RetVal;
|
||||||
Byte *input;
|
Byte *input;
|
||||||
unsigned long start_total_out;
|
unsigned long start_total_out;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "s#:compress", &input, &inplen))
|
if (!PyArg_ParseTuple(args, "s#:compress", &input, &inplen))
|
||||||
return NULL;
|
return NULL;
|
||||||
self->zst.avail_in = inplen;
|
|
||||||
self->zst.next_in = input;
|
|
||||||
if (!(RetVal = PyString_FromStringAndSize(NULL, length))) {
|
if (!(RetVal = PyString_FromStringAndSize(NULL, length))) {
|
||||||
PyErr_SetString(PyExc_MemoryError,
|
PyErr_SetString(PyExc_MemoryError,
|
||||||
"Can't allocate memory to compress data");
|
"Can't allocate memory to compress data");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
start_total_out = self->zst.total_out;
|
start_total_out = self->zst.total_out;
|
||||||
self->zst.next_out = (unsigned char *)PyString_AsString(RetVal);
|
self->zst.avail_in = inplen;
|
||||||
|
self->zst.next_in = input;
|
||||||
self->zst.avail_out = length;
|
self->zst.avail_out = length;
|
||||||
while (self->zst.avail_in != 0 && err == Z_OK)
|
self->zst.next_out = (unsigned char *)PyString_AsString(RetVal);
|
||||||
{
|
|
||||||
err = deflate(&(self->zst), Z_NO_FLUSH);
|
err = deflate(&(self->zst), Z_NO_FLUSH);
|
||||||
if (self->zst.avail_out <= 0) {
|
/* while Z_OK and the output buffer is full, there might be more output,
|
||||||
|
so extend the output buffer and try again */
|
||||||
|
while (err == Z_OK && self->zst.avail_out == 0) {
|
||||||
if (_PyString_Resize(&RetVal, length << 1) == -1) {
|
if (_PyString_Resize(&RetVal, length << 1) == -1) {
|
||||||
PyErr_SetString(PyExc_MemoryError,
|
PyErr_SetString(PyExc_MemoryError,
|
||||||
"Can't allocate memory to compress data");
|
"Can't allocate memory to compress data");
|
||||||
|
@ -427,10 +427,11 @@ PyZlib_objcompress(compobject *self, PyObject *args)
|
||||||
self->zst.next_out = (unsigned char *)PyString_AsString(RetVal) + length;
|
self->zst.next_out = (unsigned char *)PyString_AsString(RetVal) + length;
|
||||||
self->zst.avail_out = length;
|
self->zst.avail_out = length;
|
||||||
length = length << 1;
|
length = length << 1;
|
||||||
|
err = deflate(&(self->zst), Z_NO_FLUSH);
|
||||||
}
|
}
|
||||||
}
|
/* We will only get Z_BUF_ERROR if the output buffer was full but there
|
||||||
if (err != Z_OK)
|
wasn't more output when we tried again, so it is not an error condition */
|
||||||
{
|
if (err != Z_OK && err != Z_BUF_ERROR) {
|
||||||
if (self->zst.msg == Z_NULL)
|
if (self->zst.msg == Z_NULL)
|
||||||
PyErr_Format(ZlibError, "Error %i while compressing",
|
PyErr_Format(ZlibError, "Error %i while compressing",
|
||||||
err);
|
err);
|
||||||
|
@ -454,28 +455,28 @@ static char decomp_decompress__doc__[] =
|
||||||
static PyObject *
|
static PyObject *
|
||||||
PyZlib_objdecompress(compobject *self, PyObject *args)
|
PyZlib_objdecompress(compobject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
int length, err, inplen;
|
int err, inplen, length = DEFAULTALLOC;
|
||||||
PyObject *RetVal;
|
PyObject *RetVal;
|
||||||
Byte *input;
|
Byte *input;
|
||||||
unsigned long start_total_out;
|
unsigned long start_total_out;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "s#:decompress", &input, &inplen))
|
if (!PyArg_ParseTuple(args, "s#:decompress", &input, &inplen))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
if (!(RetVal = PyString_FromStringAndSize(NULL, length))) {
|
||||||
|
PyErr_SetString(PyExc_MemoryError,
|
||||||
|
"Can't allocate memory to compress data");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
start_total_out = self->zst.total_out;
|
start_total_out = self->zst.total_out;
|
||||||
RetVal = PyString_FromStringAndSize(NULL, DEFAULTALLOC);
|
|
||||||
self->zst.avail_in = inplen;
|
self->zst.avail_in = inplen;
|
||||||
self->zst.next_in = input;
|
self->zst.next_in = input;
|
||||||
self->zst.avail_out = length = DEFAULTALLOC;
|
self->zst.avail_out = length;
|
||||||
self->zst.next_out = (unsigned char *)PyString_AsString(RetVal);
|
self->zst.next_out = (unsigned char *)PyString_AsString(RetVal);
|
||||||
err = Z_OK;
|
err = inflate(&(self->zst), Z_SYNC_FLUSH);
|
||||||
|
/* while Z_OK and the output buffer is full, there might be more output,
|
||||||
while (self->zst.avail_in != 0 && err == Z_OK)
|
so extend the output buffer and try again */
|
||||||
{
|
while (err == Z_OK && self->zst.avail_out == 0) {
|
||||||
err = inflate(&(self->zst), Z_NO_FLUSH);
|
if (_PyString_Resize(&RetVal, length << 1) == -1) {
|
||||||
if (err == Z_OK && self->zst.avail_out <= 0)
|
|
||||||
{
|
|
||||||
if (_PyString_Resize(&RetVal, length << 1) == -1)
|
|
||||||
{
|
|
||||||
PyErr_SetString(PyExc_MemoryError,
|
PyErr_SetString(PyExc_MemoryError,
|
||||||
"Can't allocate memory to compress data");
|
"Can't allocate memory to compress data");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -483,11 +484,25 @@ PyZlib_objdecompress(compobject *self, PyObject *args)
|
||||||
self->zst.next_out = (unsigned char *)PyString_AsString(RetVal) + length;
|
self->zst.next_out = (unsigned char *)PyString_AsString(RetVal) + length;
|
||||||
self->zst.avail_out = length;
|
self->zst.avail_out = length;
|
||||||
length = length << 1;
|
length = length << 1;
|
||||||
|
err = inflate(&(self->zst), Z_SYNC_FLUSH);
|
||||||
}
|
}
|
||||||
|
/* The end of the compressed data has been reached, so set the unused_data
|
||||||
|
attribute to a string containing the remainder of the data in the string.
|
||||||
|
Note that this is also a logical place to call inflateEnd, but the old
|
||||||
|
behaviour of only calling it on flush() is preserved.*/
|
||||||
|
if (err == Z_STREAM_END) {
|
||||||
|
Py_XDECREF(self->unused_data); /* Free the original, empty string */
|
||||||
|
self->unused_data = PyString_FromStringAndSize(self->zst.next_in,
|
||||||
|
self->zst.avail_in);
|
||||||
|
if (self->unused_data == NULL) {
|
||||||
|
PyErr_SetString(PyExc_MemoryError,
|
||||||
|
"Can't allocate memory to unused_data");
|
||||||
|
Py_DECREF(RetVal);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
/* We will only get Z_BUF_ERROR if the output buffer was full but there
|
||||||
if (err != Z_OK && err != Z_STREAM_END)
|
wasn't more output when we tried again, so it is not an error condition */
|
||||||
{
|
} else if (err != Z_OK && err != Z_BUF_ERROR) {
|
||||||
if (self->zst.msg == Z_NULL)
|
if (self->zst.msg == Z_NULL)
|
||||||
PyErr_Format(ZlibError, "Error %i while decompressing",
|
PyErr_Format(ZlibError, "Error %i while decompressing",
|
||||||
err);
|
err);
|
||||||
|
@ -497,20 +512,6 @@ PyZlib_objdecompress(compobject *self, PyObject *args)
|
||||||
Py_DECREF(RetVal);
|
Py_DECREF(RetVal);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err == Z_STREAM_END)
|
|
||||||
{
|
|
||||||
/* The end of the compressed data has been reached, so set
|
|
||||||
the unused_data attribute to a string containing the
|
|
||||||
remainder of the data in the string. */
|
|
||||||
int pos = self->zst.next_in - input; /* Position in the string */
|
|
||||||
Py_XDECREF(self->unused_data); /* Free the original, empty string */
|
|
||||||
|
|
||||||
self->unused_data = PyString_FromStringAndSize((char *)input+pos,
|
|
||||||
inplen-pos);
|
|
||||||
if (self->unused_data == NULL) return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
_PyString_Resize(&RetVal, self->zst.total_out - start_total_out);
|
_PyString_Resize(&RetVal, self->zst.total_out - start_total_out);
|
||||||
return RetVal;
|
return RetVal;
|
||||||
}
|
}
|
||||||
|
@ -526,7 +527,7 @@ static char comp_flush__doc__[] =
|
||||||
static PyObject *
|
static PyObject *
|
||||||
PyZlib_flush(compobject *self, PyObject *args)
|
PyZlib_flush(compobject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
int length=DEFAULTALLOC, err = Z_OK;
|
int err, length=DEFAULTALLOC;
|
||||||
PyObject *RetVal;
|
PyObject *RetVal;
|
||||||
int flushmode = Z_FINISH;
|
int flushmode = Z_FINISH;
|
||||||
unsigned long start_total_out;
|
unsigned long start_total_out;
|
||||||
|
@ -536,38 +537,23 @@ PyZlib_flush(compobject *self, PyObject *args)
|
||||||
|
|
||||||
/* Flushing with Z_NO_FLUSH is a no-op, so there's no point in
|
/* Flushing with Z_NO_FLUSH is a no-op, so there's no point in
|
||||||
doing any work at all; just return an empty string. */
|
doing any work at all; just return an empty string. */
|
||||||
if (flushmode == Z_NO_FLUSH)
|
if (flushmode == Z_NO_FLUSH) {
|
||||||
{
|
|
||||||
return PyString_FromStringAndSize(NULL, 0);
|
return PyString_FromStringAndSize(NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
self->zst.avail_in = 0;
|
|
||||||
self->zst.next_in = Z_NULL;
|
|
||||||
if (!(RetVal = PyString_FromStringAndSize(NULL, length))) {
|
if (!(RetVal = PyString_FromStringAndSize(NULL, length))) {
|
||||||
PyErr_SetString(PyExc_MemoryError,
|
PyErr_SetString(PyExc_MemoryError,
|
||||||
"Can't allocate memory to compress data");
|
"Can't allocate memory to compress data");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
start_total_out = self->zst.total_out;
|
start_total_out = self->zst.total_out;
|
||||||
self->zst.next_out = (unsigned char *)PyString_AsString(RetVal);
|
self->zst.avail_in = 0;
|
||||||
self->zst.avail_out = length;
|
self->zst.avail_out = length;
|
||||||
|
self->zst.next_out = (unsigned char *)PyString_AsString(RetVal);
|
||||||
/* When flushing the zstream, there's no input data.
|
|
||||||
If zst.avail_out == 0, that means that more output space is
|
|
||||||
needed to complete the flush operation. */
|
|
||||||
while (1) {
|
|
||||||
err = deflate(&(self->zst), flushmode);
|
err = deflate(&(self->zst), flushmode);
|
||||||
|
/* while Z_OK and the output buffer is full, there might be more output,
|
||||||
/* If the output is Z_OK, and there's still room in the output
|
so extend the output buffer and try again */
|
||||||
buffer, then the flush is complete. */
|
while (err == Z_OK && self->zst.avail_out == 0) {
|
||||||
if ( (err == Z_OK) && self->zst.avail_out > 0) break;
|
|
||||||
|
|
||||||
/* A nonzero return indicates some sort of error (but see
|
|
||||||
the comment for the error handler below) */
|
|
||||||
if ( err != Z_OK ) break;
|
|
||||||
|
|
||||||
/* There's no space left for output, so increase the buffer and loop
|
|
||||||
again */
|
|
||||||
if (_PyString_Resize(&RetVal, length << 1) == -1) {
|
if (_PyString_Resize(&RetVal, length << 1) == -1) {
|
||||||
PyErr_SetString(PyExc_MemoryError,
|
PyErr_SetString(PyExc_MemoryError,
|
||||||
"Can't allocate memory to compress data");
|
"Can't allocate memory to compress data");
|
||||||
|
@ -576,14 +562,27 @@ PyZlib_flush(compobject *self, PyObject *args)
|
||||||
self->zst.next_out = (unsigned char *)PyString_AsString(RetVal) + length;
|
self->zst.next_out = (unsigned char *)PyString_AsString(RetVal) + length;
|
||||||
self->zst.avail_out = length;
|
self->zst.avail_out = length;
|
||||||
length = length << 1;
|
length = length << 1;
|
||||||
|
err = deflate(&(self->zst), flushmode);
|
||||||
}
|
}
|
||||||
|
/* If flushmode is Z_FINISH, we also have to call deflateEnd() to free
|
||||||
/* Raise an exception indicating an error. The condition for
|
various data structures. Note we should only get Z_STREAM_END when
|
||||||
detecting a error is kind of complicated; Z_OK indicates no
|
flushmode is Z_FINISH, but checking both for safety*/
|
||||||
error, but if the flushmode is Z_FINISH, then Z_STREAM_END is
|
if (err == Z_STREAM_END && flushmode == Z_FINISH) {
|
||||||
also not an error. */
|
err=deflateEnd(&(self->zst));
|
||||||
if (err!=Z_OK && !(flushmode == Z_FINISH && err == Z_STREAM_END) )
|
if (err!=Z_OK) {
|
||||||
{
|
if (self->zst.msg == Z_NULL)
|
||||||
|
PyErr_Format(ZlibError, "Error %i from deflateEnd()",
|
||||||
|
err);
|
||||||
|
else
|
||||||
|
PyErr_Format(ZlibError,"Error %i from deflateEnd(): %.200s",
|
||||||
|
err, self->zst.msg);
|
||||||
|
Py_DECREF(RetVal);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
self->is_initialised = 0;
|
||||||
|
/* We will only get Z_BUF_ERROR if the output buffer was full but there
|
||||||
|
wasn't more output when we tried again, so it is not an error condition */
|
||||||
|
} else if (err!=Z_OK && err!=Z_BUF_ERROR) {
|
||||||
if (self->zst.msg == Z_NULL)
|
if (self->zst.msg == Z_NULL)
|
||||||
PyErr_Format(ZlibError, "Error %i while flushing",
|
PyErr_Format(ZlibError, "Error %i while flushing",
|
||||||
err);
|
err);
|
||||||
|
@ -593,24 +592,6 @@ PyZlib_flush(compobject *self, PyObject *args)
|
||||||
Py_DECREF(RetVal);
|
Py_DECREF(RetVal);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If flushmode is Z_FINISH, we also have to call deflateEnd() to
|
|
||||||
free various data structures */
|
|
||||||
|
|
||||||
if (flushmode == Z_FINISH) {
|
|
||||||
err=deflateEnd(&(self->zst));
|
|
||||||
if (err!=Z_OK) {
|
|
||||||
if (self->zst.msg == Z_NULL)
|
|
||||||
PyErr_Format(ZlibError, "Error %i from deflateEnd()",
|
|
||||||
err);
|
|
||||||
else
|
|
||||||
PyErr_Format(ZlibError,
|
|
||||||
"Error %i from deflateEnd(): %.200s",
|
|
||||||
err, self->zst.msg);
|
|
||||||
Py_DECREF(RetVal);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_PyString_Resize(&RetVal, self->zst.total_out - start_total_out);
|
_PyString_Resize(&RetVal, self->zst.total_out - start_total_out);
|
||||||
return RetVal;
|
return RetVal;
|
||||||
}
|
}
|
||||||
|
@ -622,71 +603,27 @@ static char decomp_flush__doc__[] =
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
PyZlib_unflush(compobject *self, PyObject *args)
|
PyZlib_unflush(compobject *self, PyObject *args)
|
||||||
|
/*decompressor flush is a no-op because all pending data would have been
|
||||||
|
flushed by the decompress method. However, this routine previously called
|
||||||
|
inflateEnd, causing any further decompress or flush calls to raise
|
||||||
|
exceptions. This behaviour has been preserved.*/
|
||||||
{
|
{
|
||||||
int length=0, err;
|
int err;
|
||||||
PyObject *RetVal;
|
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, ""))
|
if (!PyArg_ParseTuple(args, ""))
|
||||||
return NULL;
|
return NULL;
|
||||||
if (!(RetVal = PyString_FromStringAndSize(NULL, DEFAULTALLOC)))
|
|
||||||
{
|
|
||||||
PyErr_SetString(PyExc_MemoryError,
|
|
||||||
"Can't allocate memory to decompress data");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
self->zst.avail_in=0;
|
|
||||||
self->zst.next_out = (unsigned char *)PyString_AsString(RetVal);
|
|
||||||
length = self->zst.avail_out = DEFAULTALLOC;
|
|
||||||
|
|
||||||
/* I suspect that Z_BUF_ERROR is the only error code we need to check for
|
|
||||||
in the following loop, but will leave the Z_OK in for now to avoid
|
|
||||||
destabilizing this function. --amk */
|
|
||||||
err = Z_OK;
|
|
||||||
while ( err == Z_OK )
|
|
||||||
{
|
|
||||||
err = inflate(&(self->zst), Z_FINISH);
|
|
||||||
if ( ( err == Z_OK || err == Z_BUF_ERROR ) && self->zst.avail_out == 0)
|
|
||||||
{
|
|
||||||
if (_PyString_Resize(&RetVal, length << 1) == -1)
|
|
||||||
{
|
|
||||||
PyErr_SetString(PyExc_MemoryError,
|
|
||||||
"Can't allocate memory to decompress data");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
self->zst.next_out = (unsigned char *)PyString_AsString(RetVal) + length;
|
|
||||||
self->zst.avail_out = length;
|
|
||||||
length = length << 1;
|
|
||||||
err = Z_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (err!=Z_STREAM_END)
|
|
||||||
{
|
|
||||||
if (self->zst.msg == Z_NULL)
|
|
||||||
PyErr_Format(ZlibError, "Error %i while decompressing",
|
|
||||||
err);
|
|
||||||
else
|
|
||||||
PyErr_Format(ZlibError, "Error %i while decompressing: %.200s",
|
|
||||||
err, self->zst.msg);
|
|
||||||
Py_DECREF(RetVal);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
err=inflateEnd(&(self->zst));
|
err=inflateEnd(&(self->zst));
|
||||||
if (err!=Z_OK)
|
if (err!=Z_OK) {
|
||||||
{
|
|
||||||
if (self->zst.msg == Z_NULL)
|
if (self->zst.msg == Z_NULL)
|
||||||
PyErr_Format(ZlibError,
|
PyErr_Format(ZlibError, "Error %i from inflateEnd()",
|
||||||
"Error %i while flushing decompression object",
|
|
||||||
err);
|
err);
|
||||||
else
|
else
|
||||||
PyErr_Format(ZlibError,
|
PyErr_Format(ZlibError, "Error %i from inflateEnd(): %.200s",
|
||||||
"Error %i while flushing decompression object: %.200s",
|
|
||||||
err, self->zst.msg);
|
err, self->zst.msg);
|
||||||
Py_DECREF(RetVal);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
_PyString_Resize(&RetVal,
|
self->is_initialised = 0;
|
||||||
(char *)self->zst.next_out - PyString_AsString(RetVal));
|
return PyString_FromStringAndSize(NULL, 0);
|
||||||
return RetVal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyMethodDef comp_methods[] =
|
static PyMethodDef comp_methods[] =
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue