[Jython-checkins] jython: Do not deserialize PyFunction objects. Fixes #2454

jim.baker jython-checkins at python.org
Mon Feb 1 22:37:15 EST 2016


https://hg.python.org/jython/rev/d06e29d100c0
changeset:   7887:d06e29d100c0
user:        Jim Baker <jim.baker at rackspace.com>
date:        Mon Feb 01 20:37:01 2016 -0700
summary:
  Do not deserialize PyFunction objects. Fixes #2454

Instead use standard Python pickling; or subclass PyFunction.

files:
  Lib/test/test_java_integration.py   |  26 ++++++++++++--
  Lib/test/test_new.py                |  31 ++++++++--------
  src/org/python/core/PyBytecode.java |   6 +++
  src/org/python/core/PyFunction.java |   3 +
  4 files changed, 46 insertions(+), 20 deletions(-)


diff --git a/Lib/test/test_java_integration.py b/Lib/test/test_java_integration.py
--- a/Lib/test/test_java_integration.py
+++ b/Lib/test/test_java_integration.py
@@ -14,8 +14,9 @@
 from collections import deque
 from test import test_support
 
-from java.lang import (ClassCastException, ExceptionInInitializerError, String, Runnable, System,
-        Runtime, Math, Byte)
+from java.lang import (
+    ClassCastException, ExceptionInInitializerError, UnsupportedOperationException,
+    String, Runnable, System, Runtime, Math, Byte)
 from java.math import BigDecimal, BigInteger
 from java.net import URI
 from java.io import (ByteArrayInputStream, ByteArrayOutputStream, File, FileInputStream,
@@ -656,13 +657,30 @@
         self.assertEqual(date_list, roundtrip_serialization(date_list))
 
     def test_java_serialization_pycode(self):
-
         def universal_answer():
             return 42
 
         serialized_code = roundtrip_serialization(universal_answer.func_code)
         self.assertEqual(eval(serialized_code), universal_answer())
 
+    def test_java_serialization_pyfunction(self):
+        # Not directly supported due to lack of general utility
+        # (globals will usually be in the function object in
+        # func_globals), and problems with unserialization
+        # vulnerabilities. Users can always subclass from PyFunction
+        # for specific cases, as seen in PyCascading
+        import new
+        def f():
+            return 6 * 7 + max(0, 1, 2)
+        # However, using the new module, it's possible to create a
+        # function with no globals, which means the globals will come
+        # from the current context
+        g = new.function(f.func_code, {}, "g")
+        # But still forbid Java deserialization of this function
+        # object. Use pickling or other support instead.
+        with self.assertRaises(UnsupportedOperationException):
+            roundtrip_serialization(g)
+
     def test_builtin_names(self):
         import __builtin__
         names = [x for x in dir(__builtin__)]
@@ -872,7 +890,7 @@
         future.get()
         self.assertEqual(x, [42])
 
-    @unittest.skip("FIXME: not working")
+    @unittest.skip("FIXME: not working; see http://bugs.jython.org/issue2115")
     def test_callable_object(self):
         callable_obj = CallableObject()
         future = self.executor.submit(callable_obj)
diff --git a/Lib/test/test_new.py b/Lib/test/test_new.py
--- a/Lib/test/test_new.py
+++ b/Lib/test/test_new.py
@@ -24,18 +24,10 @@
         c = new.instance(C, {'yolks': 3})
 
         o = new.instance(C)
-
-        # __dict__ is a non dict mapping in Jython
-        if test_support.is_jython:
-            self.assertEqual(len(o.__dict__), 0, "new __dict__ should be empty")
-        else:
-            self.assertEqual(o.__dict__, {}, "new __dict__ should be empty")
+        self.assertEqual(o.__dict__, {}, "new __dict__ should be empty")
         del o
         o = new.instance(C, None)
-        if test_support.is_jython:
-            self.assertEqual(len(o.__dict__), 0, "new __dict__ should be empty")
-        else:
-            self.assertEqual(o.__dict__, {}, "new __dict__ should be empty")
+        self.assertEqual(o.__dict__, {}, "new __dict__ should be empty")
         del o
 
         def break_yolks(self):
@@ -109,7 +101,14 @@
         test_closure(g, (1, 1), ValueError) # closure is wrong size
         test_closure(f, g.func_closure, ValueError) # no closure needed
 
-    if hasattr(new, 'code') and not test_support.is_jython:
+    # [Obsolete] Note: Jython will never have new.code()
+    #
+    # Who said that?!!! guess what, we do! :)
+    # 
+    # Unfortunately we still need a way to compile to Python bytecode,
+    # so support is still incomplete, as seen in the fact that we need
+    # to get values from CPython 2.7.
+    if hasattr(new, 'code'):
         def test_code(self):
             # bogus test of new.code()
             def f(a): pass
@@ -117,16 +116,16 @@
             c = f.func_code
             argcount = c.co_argcount
             nlocals = c.co_nlocals
-            stacksize = c.co_stacksize
+            stacksize = 1  # TODO c.co_stacksize
             flags = c.co_flags
-            codestring = c.co_code
-            constants = c.co_consts
-            names = c.co_names
+            codestring = 'd\x00\x00S'  # TODO c.co_code
+            constants = (None,)  # TODO c.co_consts
+            names = ()  # TODO c.co_names
             varnames = c.co_varnames
             filename = c.co_filename
             name = c.co_name
             firstlineno = c.co_firstlineno
-            lnotab = c.co_lnotab
+            lnotab = '' # TODO c.co_lnotab, but also see http://bugs.jython.org/issue1638
             freevars = c.co_freevars
             cellvars = c.co_cellvars
 
diff --git a/src/org/python/core/PyBytecode.java b/src/org/python/core/PyBytecode.java
--- a/src/org/python/core/PyBytecode.java
+++ b/src/org/python/core/PyBytecode.java
@@ -66,6 +66,12 @@
 
         debug = defaultDebug;
 
+        if (argcount < 0) {
+            throw Py.ValueError("code: argcount must not be negative");
+        } else if (nlocals < 0) {
+            throw Py.ValueError("code: nlocals must not be negative");
+        }
+
         co_argcount = nargs = argcount;
         co_varnames = varnames;
         co_nlocals = nlocals; // maybe assert = varnames.length;
diff --git a/src/org/python/core/PyFunction.java b/src/org/python/core/PyFunction.java
--- a/src/org/python/core/PyFunction.java
+++ b/src/org/python/core/PyFunction.java
@@ -545,6 +545,9 @@
     @Override
     public boolean isSequenceType() { return false; }
 
+    private Object readResolve() {
+        throw new UnsupportedOperationException();
+    }
 
     /* Traverseproc implementation */
     @Override

-- 
Repository URL: https://hg.python.org/jython


More information about the Jython-checkins mailing list