#19395: Raise exception when pickling a (BZ2|LZMA)(Compressor|Decompressor).
authorNadeem Vawda <nadeem.vawda@gmail.com>
Mon, 28 Oct 2013 20:35:23 +0000 (21:35 +0100)
committerNadeem Vawda <nadeem.vawda@gmail.com>
Mon, 28 Oct 2013 20:35:23 +0000 (21:35 +0100)
The underlying C libraries provide no mechanism for serializing compressor and
decompressor objects, so actually pickling these classes is impractical.
Previously, these objects would be pickled without error, but attempting to use
a deserialized instance would segfault the interpreter.

Lib/test/test_bz2.py
Lib/test/test_lzma.py
Misc/NEWS
Modules/_bz2module.c
Modules/_lzmamodule.c

index 912fac1c3344f4359ddb81d9af333148a2c9ed5e..6764e59553c4b9403ff89692436fcc2712ab56ad 100644 (file)
@@ -5,6 +5,7 @@ from test.support import TESTFN, bigmemtest, _4G
 import unittest
 from io import BytesIO
 import os
+import pickle
 import random
 import subprocess
 import sys
@@ -621,6 +622,11 @@ class BZ2CompressorTest(BaseTest):
         finally:
             data = None
 
+    def testPickle(self):
+        with self.assertRaises(TypeError):
+            pickle.dumps(BZ2Compressor())
+
+
 class BZ2DecompressorTest(BaseTest):
     def test_Constructor(self):
         self.assertRaises(TypeError, BZ2Decompressor, 42)
@@ -672,6 +678,10 @@ class BZ2DecompressorTest(BaseTest):
             compressed = None
             decompressed = None
 
+    def testPickle(self):
+        with self.assertRaises(TypeError):
+            pickle.dumps(BZ2Decompressor())
+
 
 class CompressDecompressTest(BaseTest):
     def testCompress(self):
index a13cf3bd09aa66d35011cf3a1c6f95b4cb4333df..ad9045604e3bcd1cdc099f8703be9209aed7e803 100644 (file)
@@ -1,5 +1,6 @@
 from io import BytesIO, UnsupportedOperation
 import os
+import pickle
 import random
 import unittest
 
@@ -216,6 +217,14 @@ class CompressorDecompressorTestCase(unittest.TestCase):
         finally:
             input = cdata = ddata = None
 
+    # Pickling raises an exception; there's no way to serialize an lzma_stream.
+
+    def test_pickle(self):
+        with self.assertRaises(TypeError):
+            pickle.dumps(LZMACompressor())
+        with self.assertRaises(TypeError):
+            pickle.dumps(LZMADecompressor())
+
 
 class CompressDecompressFunctionTestCase(unittest.TestCase):
 
index a911331d9fc087adf6c9d2dfb320e52d797b60a4..e83016719309e7320e366f6e9ee6480fded9ed58 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -92,6 +92,10 @@ Core and Builtins
 Library
 -------
 
+- Issue #19395: Raise an exception when attempting to pickle a bz2 or lzma
+  compressor/decompressor object, rather than creating a pickle that would
+  cause a segfault when loaded and used.
+
 - Issue #19227: Try to fix deadlocks caused by re-seeding then OpenSSL
   pseudo-random number generator on fork().
 
index 4eee5a2feeb9db7aa98bc48339d2a461df6e4aa6..3f7a6cf026202be84265d2565d61968cfa6aecdd 100644 (file)
@@ -250,6 +250,14 @@ BZ2Compressor_flush(BZ2Compressor *self, PyObject *noargs)
     return result;
 }
 
+static PyObject *
+BZ2Compressor_getstate(BZ2Compressor *self, PyObject *noargs)
+{
+    PyErr_Format(PyExc_TypeError, "cannot serialize '%s' object",
+                 Py_TYPE(self)->tp_name);
+    return NULL;
+}
+
 static int
 BZ2Compressor_init(BZ2Compressor *self, PyObject *args, PyObject *kwargs)
 {
@@ -298,10 +306,11 @@ BZ2Compressor_dealloc(BZ2Compressor *self)
 }
 
 static PyMethodDef BZ2Compressor_methods[] = {
-    {"compress", (PyCFunction)BZ2Compressor_compress, METH_VARARGS,
+    {"compress",     (PyCFunction)BZ2Compressor_compress, METH_VARARGS,
      BZ2Compressor_compress__doc__},
-    {"flush",    (PyCFunction)BZ2Compressor_flush,    METH_NOARGS,
+    {"flush",        (PyCFunction)BZ2Compressor_flush,    METH_NOARGS,
      BZ2Compressor_flush__doc__},
+    {"__getstate__", (PyCFunction)BZ2Compressor_getstate, METH_NOARGS},
     {NULL}
 };
 
@@ -452,6 +461,14 @@ BZ2Decompressor_decompress(BZ2Decompressor *self, PyObject *args)
     return result;
 }
 
+static PyObject *
+BZ2Decompressor_getstate(BZ2Decompressor *self, PyObject *noargs)
+{
+    PyErr_Format(PyExc_TypeError, "cannot serialize '%s' object",
+                 Py_TYPE(self)->tp_name);
+    return NULL;
+}
+
 static int
 BZ2Decompressor_init(BZ2Decompressor *self, PyObject *args, PyObject *kwargs)
 {
@@ -502,6 +519,7 @@ BZ2Decompressor_dealloc(BZ2Decompressor *self)
 static PyMethodDef BZ2Decompressor_methods[] = {
     {"decompress", (PyCFunction)BZ2Decompressor_decompress, METH_VARARGS,
      BZ2Decompressor_decompress__doc__},
+    {"__getstate__", (PyCFunction)BZ2Decompressor_getstate, METH_NOARGS},
     {NULL}
 };
 
index b482a7767db18887babbe9381cb6d5bcfd207d97..643616052c5d07b53a04169263b366eae2e27ece 100644 (file)
@@ -546,6 +546,14 @@ Compressor_flush(Compressor *self, PyObject *noargs)
     return result;
 }
 
+static PyObject *
+Compressor_getstate(Compressor *self, PyObject *noargs)
+{
+    PyErr_Format(PyExc_TypeError, "cannot serialize '%s' object",
+                 Py_TYPE(self)->tp_name);
+    return NULL;
+}
+
 static int
 Compressor_init_xz(lzma_stream *lzs, int check, uint32_t preset,
                    PyObject *filterspecs)
@@ -712,6 +720,7 @@ static PyMethodDef Compressor_methods[] = {
      Compressor_compress_doc},
     {"flush", (PyCFunction)Compressor_flush, METH_NOARGS,
      Compressor_flush_doc},
+    {"__getstate__", (PyCFunction)Compressor_getstate, METH_NOARGS},
     {NULL}
 };
 
@@ -869,6 +878,14 @@ Decompressor_decompress(Decompressor *self, PyObject *args)
     return result;
 }
 
+static PyObject *
+Decompressor_getstate(Decompressor *self, PyObject *noargs)
+{
+    PyErr_Format(PyExc_TypeError, "cannot serialize '%s' object",
+                 Py_TYPE(self)->tp_name);
+    return NULL;
+}
+
 static int
 Decompressor_init_raw(lzma_stream *lzs, PyObject *filterspecs)
 {
@@ -991,6 +1008,7 @@ Decompressor_dealloc(Decompressor *self)
 static PyMethodDef Decompressor_methods[] = {
     {"decompress", (PyCFunction)Decompressor_decompress, METH_VARARGS,
      Decompressor_decompress_doc},
+    {"__getstate__", (PyCFunction)Decompressor_getstate, METH_NOARGS},
     {NULL}
 };