Blame SOURCES/00235-JSON-decoder-lone-surrogates-fix.patch

ae2451
From 90986ef48c0df602ab38aa831a24e99e9ed61e7e Mon Sep 17 00:00:00 2001
ae2451
From: Charalampos Stratakis <cstratak@redhat.com>
ae2451
Date: Mon, 4 Apr 2016 15:55:28 +0200
ae2451
Subject: [PATCH] JSON decoder now accepts lone surrogates
ae2451
ae2451
---
ae2451
 Lib/json/decoder.py               | 35 ++++++++++++------------
ae2451
 Lib/json/tests/test_scanstring.py | 56 ++++++++++++++++++++++++++++++++++++---
ae2451
 Modules/_json.c                   | 49 +++++++++-------------------------
ae2451
 3 files changed, 83 insertions(+), 57 deletions(-)
ae2451
ae2451
diff --git a/Lib/json/decoder.py b/Lib/json/decoder.py
ae2451
index dfcc628..1b43238 100644
ae2451
--- a/Lib/json/decoder.py
ae2451
+++ b/Lib/json/decoder.py
ae2451
@@ -62,6 +62,16 @@ BACKSLASH = {
ae2451
ae2451
 DEFAULT_ENCODING = "utf-8"
ae2451
ae2451
+def _decode_uXXXX(s, pos):
ae2451
+    esc = s[pos + 1:pos + 5]
ae2451
+    if len(esc) == 4 and esc[1] not in 'xX':
ae2451
+        try:
ae2451
+            return int(esc, 16)
ae2451
+        except ValueError:
ae2451
+            pass
ae2451
+    msg = "Invalid \\uXXXX escape"
ae2451
+    raise ValueError(errmsg(msg, s, pos))
ae2451
+
ae2451
 def py_scanstring(s, end, encoding=None, strict=True,
ae2451
         _b=BACKSLASH, _m=STRINGCHUNK.match):
ae2451
     """Scan the string s for a JSON string. End is the index of the
ae2451
@@ -116,25 +126,16 @@ def py_scanstring(s, end, encoding=None, strict=True,
ae2451
             end += 1
ae2451
         else:
ae2451
             # Unicode escape sequence
ae2451
-            esc = s[end + 1:end + 5]
ae2451
-            next_end = end + 5
ae2451
-            if len(esc) != 4:
ae2451
-                msg = "Invalid \\uXXXX escape"
ae2451
-                raise ValueError(errmsg(msg, s, end))
ae2451
-            uni = int(esc, 16)
ae2451
+            uni = _decode_uXXXX(s, end)
ae2451
+            end += 5
ae2451
             # Check for surrogate pair on UCS-4 systems
ae2451
-            if 0xd800 <= uni <= 0xdbff and sys.maxunicode > 65535:
ae2451
-                msg = "Invalid \\uXXXX\\uXXXX surrogate pair"
ae2451
-                if not s[end + 5:end + 7] == '\\u':
ae2451
-                    raise ValueError(errmsg(msg, s, end))
ae2451
-                esc2 = s[end + 7:end + 11]
ae2451
-                if len(esc2) != 4:
ae2451
-                    raise ValueError(errmsg(msg, s, end))
ae2451
-                uni2 = int(esc2, 16)
ae2451
-                uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00))
ae2451
-                next_end += 6
ae2451
+            if sys.maxunicode > 65535 and \
ae2451
+               0xd800 <= uni <= 0xdbff and s[end:end + 2] == '\\u':
ae2451
+                uni2 = _decode_uXXXX(s, end + 1)
ae2451
+                if 0xdc00 <= uni2 <= 0xdfff:
ae2451
+                    uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00))
ae2451
+                    end += 6
ae2451
             char = unichr(uni)
ae2451
-            end = next_end
ae2451
         # Append the unescaped character
ae2451
         _append(char)
ae2451
     return u''.join(chunks), end
ae2451
diff --git a/Lib/json/tests/test_scanstring.py b/Lib/json/tests/test_scanstring.py
ae2451
index 4fef8cb..ed80a41 100644
ae2451
--- a/Lib/json/tests/test_scanstring.py
ae2451
+++ b/Lib/json/tests/test_scanstring.py
ae2451
@@ -5,10 +5,6 @@ from json.tests import PyTest, CTest
ae2451
 class TestScanstring(object):
ae2451
     def test_scanstring(self):
ae2451
         scanstring = self.json.decoder.scanstring
ae2451
-        self.assertEqual(
ae2451
-            scanstring('"z\\ud834\\udd20x"', 1, None, True),
ae2451
-            (u'z\U0001d120x', 16))
ae2451
-
ae2451
         if sys.maxunicode == 65535:
ae2451
             self.assertEqual(
ae2451
                 scanstring(u'"z\U0001d120x"', 1, None, True),
ae2451
@@ -94,6 +90,58 @@ class TestScanstring(object):
ae2451
             scanstring('["Bad value", truth]', 2, None, True),
ae2451
             (u'Bad value', 12))
ae2451
ae2451
+    def test_surrogates(self):
ae2451
+        scanstring = self.json.decoder.scanstring
ae2451
+        def assertScan(given, expect):
ae2451
+            self.assertEqual(scanstring(given, 1, None, True),
ae2451
+                             (expect, len(given)))
ae2451
+            if not isinstance(given, unicode):
ae2451
+                given = unicode(given)
ae2451
+                self.assertEqual(scanstring(given, 1, None, True),
ae2451
+                                 (expect, len(given)))
ae2451
+
ae2451
+        surrogates = unichr(0xd834) + unichr(0xdd20)
ae2451
+        assertScan('"z\\ud834\\u0079x"', u'z\ud834yx')
ae2451
+        assertScan('"z\\ud834\\udd20x"', u'z\U0001d120x')
ae2451
+        assertScan('"z\\ud834\\ud834\\udd20x"', u'z\ud834\U0001d120x')
ae2451
+        assertScan('"z\\ud834x"', u'z\ud834x')
ae2451
+        assertScan(u'"z\\ud834\udd20x12345"', u'z%sx12345' % surrogates)
ae2451
+        assertScan('"z\\udd20x"', u'z\udd20x')
ae2451
+        assertScan(u'"z\ud834\udd20x"', u'z\ud834\udd20x')
ae2451
+        assertScan(u'"z\ud834\\udd20x"', u'z%sx' % surrogates)
ae2451
+        assertScan(u'"z\ud834x"', u'z\ud834x')
ae2451
+
ae2451
+    def test_bad_escapes(self):
ae2451
+        scanstring = self.json.decoder.scanstring
ae2451
+        bad_escapes = [
ae2451
+            '"\\"',
ae2451
+            '"\\x"',
ae2451
+            '"\\u"',
ae2451
+            '"\\u0"',
ae2451
+            '"\\u01"',
ae2451
+            '"\\u012"',
ae2451
+            '"\\uz012"',
ae2451
+            '"\\u0z12"',
ae2451
+            '"\\u01z2"',
ae2451
+            '"\\u012z"',
ae2451
+            '"\\u0x12"',
ae2451
+            '"\\u0X12"',
ae2451
+            '"\\ud834\\"',
ae2451
+            '"\\ud834\\u"',
ae2451
+            '"\\ud834\\ud"',
ae2451
+            '"\\ud834\\udd"',
ae2451
+            '"\\ud834\\udd2"',
ae2451
+            '"\\ud834\\uzdd2"',
ae2451
+            '"\\ud834\\udzd2"',
ae2451
+            '"\\ud834\\uddz2"',
ae2451
+            '"\\ud834\\udd2z"',
ae2451
+            '"\\ud834\\u0x20"',
ae2451
+            '"\\ud834\\u0X20"',
ae2451
+        ]
ae2451
+        for s in bad_escapes:
ae2451
+            with self.assertRaises(ValueError):
ae2451
+                scanstring(s, 1, None, True)
ae2451
+
ae2451
     def test_issue3623(self):
ae2451
         self.assertRaises(ValueError, self.json.decoder.scanstring, b"xxx", 1,
ae2451
                           "xxx")
ae2451
diff --git a/Modules/_json.c b/Modules/_json.c
ae2451
index 7c925fd..56d9ee4 100644
ae2451
--- a/Modules/_json.c
ae2451
+++ b/Modules/_json.c
ae2451
@@ -524,16 +524,10 @@ scanstring_str(PyObject *pystr, Py_ssize_t end, char *encoding, int strict, Py_s
ae2451
             }
ae2451
 #ifdef Py_UNICODE_WIDE
ae2451
             /* Surrogate pair */
ae2451
-            if ((c & 0xfc00) == 0xd800) {
ae2451
+            if ((c & 0xfc00) == 0xd800 && end + 6 < len &&
ae2451
+                buf[next++] == '\\' &&
ae2451
+                buf[next++] == 'u') {
ae2451
                 Py_UNICODE c2 = 0;
ae2451
-                if (end + 6 >= len) {
ae2451
-                    raise_errmsg("Unpaired high surrogate", pystr, end - 5);
ae2451
-                    goto bail;
ae2451
-                }
ae2451
-                if (buf[next++] != '\\' || buf[next++] != 'u') {
ae2451
-                    raise_errmsg("Unpaired high surrogate", pystr, end - 5);
ae2451
-                    goto bail;
ae2451
-                }
ae2451
                 end += 6;
ae2451
                 /* Decode 4 hex digits */
ae2451
                 for (; next < end; next++) {
ae2451
@@ -554,15 +548,10 @@ scanstring_str(PyObject *pystr, Py_ssize_t end, char *encoding, int strict, Py_s
ae2451
                             goto bail;
ae2451
                     }
ae2451
                 }
ae2451
-                if ((c2 & 0xfc00) != 0xdc00) {
ae2451
-                    raise_errmsg("Unpaired high surrogate", pystr, end - 5);
ae2451
-                    goto bail;
ae2451
-                }
ae2451
-                c = 0x10000 + (((c - 0xd800) << 10) | (c2 - 0xdc00));
ae2451
-            }
ae2451
-            else if ((c & 0xfc00) == 0xdc00) {
ae2451
-                raise_errmsg("Unpaired low surrogate", pystr, end - 5);
ae2451
-                goto bail;
ae2451
+                if ((c2 & 0xfc00) == 0xdc00)
ae2451
+                    c = 0x10000 + (((c - 0xd800) << 10) | (c2 - 0xdc00));
ae2451
+                else
ae2451
+                    end -= 6;
ae2451
             }
ae2451
 #endif
ae2451
         }
ae2451
@@ -703,16 +692,9 @@ scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next
ae2451
             }
ae2451
 #ifdef Py_UNICODE_WIDE
ae2451
             /* Surrogate pair */
ae2451
-            if ((c & 0xfc00) == 0xd800) {
ae2451
+            if ((c & 0xfc00) == 0xd800 && end + 6 < len &&
ae2451
+                buf[next++] == '\\' && buf[next++] == 'u') {
ae2451
                 Py_UNICODE c2 = 0;
ae2451
-                if (end + 6 >= len) {
ae2451
-                    raise_errmsg("Unpaired high surrogate", pystr, end - 5);
ae2451
-                    goto bail;
ae2451
-                }
ae2451
-                if (buf[next++] != '\\' || buf[next++] != 'u') {
ae2451
-                    raise_errmsg("Unpaired high surrogate", pystr, end - 5);
ae2451
-                    goto bail;
ae2451
-                }
ae2451
                 end += 6;
ae2451
                 /* Decode 4 hex digits */
ae2451
                 for (; next < end; next++) {
ae2451
@@ -733,15 +715,10 @@ scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next
ae2451
                             goto bail;
ae2451
                     }
ae2451
                 }
ae2451
-                if ((c2 & 0xfc00) != 0xdc00) {
ae2451
-                    raise_errmsg("Unpaired high surrogate", pystr, end - 5);
ae2451
-                    goto bail;
ae2451
-                }
ae2451
-                c = 0x10000 + (((c - 0xd800) << 10) | (c2 - 0xdc00));
ae2451
-            }
ae2451
-            else if ((c & 0xfc00) == 0xdc00) {
ae2451
-                raise_errmsg("Unpaired low surrogate", pystr, end - 5);
ae2451
-                goto bail;
ae2451
+                if ((c2 & 0xfc00) == 0xdc00)
ae2451
+                    c = 0x10000 + (((c - 0xd800) << 10) | (c2 - 0xdc00));
ae2451
+                else
ae2451
+                    end -= 6;
ae2451
             }
ae2451
 #endif
ae2451
         }
ae2451
--
ae2451
2.5.5
ae2451