From: Markus Koschany <apo@debian.org>
Date: Fri, 16 Jun 2017 21:29:16 +0200
Subject: CVE-2016-4000

Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=864859
Bug-Upstream: http://bugs.jython.org/issue2454
Origin: https://hg.python.org/jython/rev/d06e29d100c0
---
 Lib/test/test_java_integration.py   | 24 +++++++++++++++++++++---
 src/org/python/core/PyBytecode.java |  6 ++++++
 src/org/python/core/PyFunction.java |  4 ++++
 3 files changed, 31 insertions(+), 3 deletions(-)

diff --git a/Lib/test/test_java_integration.py b/Lib/test/test_java_integration.py
index 37f158f..2314470 100644
--- a/Lib/test/test_java_integration.py
+++ b/Lib/test/test_java_integration.py
@@ -9,8 +9,9 @@ import re
 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.io import (ByteArrayInputStream, ByteArrayOutputStream, File, FileInputStream,
                      FileNotFoundException, FileOutputStream, FileWriter, ObjectInputStream,
@@ -526,13 +527,30 @@ class SerializationTest(unittest.TestCase):
         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__)]
diff --git a/src/org/python/core/PyBytecode.java b/src/org/python/core/PyBytecode.java
index 9418fe8..ba12e4c 100644
--- a/src/org/python/core/PyBytecode.java
+++ b/src/org/python/core/PyBytecode.java
@@ -66,6 +66,12 @@ public class PyBytecode extends PyBaseCode {
 
         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
index 18de08d..8042163 100644
--- a/src/org/python/core/PyFunction.java
+++ b/src/org/python/core/PyFunction.java
@@ -450,4 +450,8 @@ public class PyFunction extends PyObject implements InvocationHandler {
 
     @Override
     public boolean isSequenceType() { return false; }
+
+    private Object readResolve() {
+        throw new UnsupportedOperationException();
+    }
 }
