[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