https://github.com/python/cpython/commit/ce76b547f94de6b1c9c74657b4e8f150365ad76f
commit: ce76b547f94de6b1c9c74657b4e8f150365ad76f
branch: main
author: Justin Applegate <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2024-12-11T12:37:59Z
summary:

gh-126992: Change pickle code to base 10 for load_long and load_int (GH-127042)

Co-authored-by: Serhiy Storchaka <[email protected]>

files:
A Misc/NEWS.d/next/Library/2024-11-20-21-20-56.gh-issue-126992.RbU0FZ.rst
M Lib/pickle.py
M Lib/test/pickletester.py
M Lib/test/test_pickletools.py
M Modules/_pickle.c

diff --git a/Lib/pickle.py b/Lib/pickle.py
index 25dadb3f75a573..1920973e3f83e9 100644
--- a/Lib/pickle.py
+++ b/Lib/pickle.py
@@ -1387,7 +1387,7 @@ def load_int(self):
         elif data == TRUE[1:]:
             val = True
         else:
-            val = int(data, 0)
+            val = int(data)
         self.append(val)
     dispatch[INT[0]] = load_int
 
@@ -1407,7 +1407,7 @@ def load_long(self):
         val = self.readline()[:-1]
         if val and val[-1] == b'L'[0]:
             val = val[:-1]
-        self.append(int(val, 0))
+        self.append(int(val))
     dispatch[LONG[0]] = load_long
 
     def load_long1(self):
diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py
index cf020a48b81cfa..bdc7ef62943a28 100644
--- a/Lib/test/pickletester.py
+++ b/Lib/test/pickletester.py
@@ -1012,6 +1012,26 @@ def test_constants(self):
         self.assertIs(self.loads(b'I01\n.'), True)
         self.assertIs(self.loads(b'I00\n.'), False)
 
+    def test_zero_padded_integers(self):
+        self.assertEqual(self.loads(b'I010\n.'), 10)
+        self.assertEqual(self.loads(b'I-010\n.'), -10)
+        self.assertEqual(self.loads(b'I0010\n.'), 10)
+        self.assertEqual(self.loads(b'I-0010\n.'), -10)
+        self.assertEqual(self.loads(b'L010\n.'), 10)
+        self.assertEqual(self.loads(b'L-010\n.'), -10)
+        self.assertEqual(self.loads(b'L0010\n.'), 10)
+        self.assertEqual(self.loads(b'L-0010\n.'), -10)
+        self.assertEqual(self.loads(b'L010L\n.'), 10)
+        self.assertEqual(self.loads(b'L-010L\n.'), -10)
+
+    def test_nondecimal_integers(self):
+        self.assertRaises(ValueError, self.loads, b'I0b10\n.')
+        self.assertRaises(ValueError, self.loads, b'I0o10\n.')
+        self.assertRaises(ValueError, self.loads, b'I0x10\n.')
+        self.assertRaises(ValueError, self.loads, b'L0b10L\n.')
+        self.assertRaises(ValueError, self.loads, b'L0o10L\n.')
+        self.assertRaises(ValueError, self.loads, b'L0x10L\n.')
+
     def test_empty_bytestring(self):
         # issue 11286
         empty = self.loads(b'\x80\x03U\x00q\x00.', encoding='koi8-r')
diff --git a/Lib/test/test_pickletools.py b/Lib/test/test_pickletools.py
index 265dc497ccb86c..a178d3353eecdf 100644
--- a/Lib/test/test_pickletools.py
+++ b/Lib/test/test_pickletools.py
@@ -443,6 +443,43 @@ def test_persid(self):
 highest protocol among opcodes = 0
 ''')
 
+    def test_constants(self):
+        self.check_dis(b"(NI00\nI01\n\x89\x88t.", '''\
+    0: (    MARK
+    1: N        NONE
+    2: I        INT        False
+    6: I        INT        True
+   10: \\x89     NEWFALSE
+   11: \\x88     NEWTRUE
+   12: t        TUPLE      (MARK at 0)
+   13: .    STOP
+highest protocol among opcodes = 2
+''')
+
+    def test_integers(self):
+        self.check_dis(b"(I0\nI1\nI10\nI011\nL12\nL13L\nL014\nL015L\nt.", '''\
+    0: (    MARK
+    1: I        INT        0
+    4: I        INT        1
+    7: I        INT        10
+   11: I        INT        11
+   16: L        LONG       12
+   20: L        LONG       13
+   25: L        LONG       14
+   30: L        LONG       15
+   36: t        TUPLE      (MARK at 0)
+   37: .    STOP
+highest protocol among opcodes = 0
+''')
+
+    def test_nondecimal_integers(self):
+        self.check_dis_error(b'I0b10\n.', '', 'invalid literal for int')
+        self.check_dis_error(b'I0o10\n.', '', 'invalid literal for int')
+        self.check_dis_error(b'I0x10\n.', '', 'invalid literal for int')
+        self.check_dis_error(b'L0b10L\n.', '', 'invalid literal for int')
+        self.check_dis_error(b'L0o10L\n.', '', 'invalid literal for int')
+        self.check_dis_error(b'L0x10L\n.', '', 'invalid literal for int')
+
 
 class MiscTestCase(unittest.TestCase):
     def test__all__(self):
diff --git 
a/Misc/NEWS.d/next/Library/2024-11-20-21-20-56.gh-issue-126992.RbU0FZ.rst 
b/Misc/NEWS.d/next/Library/2024-11-20-21-20-56.gh-issue-126992.RbU0FZ.rst
new file mode 100644
index 00000000000000..526785f68cc807
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-11-20-21-20-56.gh-issue-126992.RbU0FZ.rst
@@ -0,0 +1 @@
+Fix LONG and INT opcodes to only use base 10 for string to integer conversion 
in :mod:`pickle`.
diff --git a/Modules/_pickle.c b/Modules/_pickle.c
index 2696f38046121f..599b5f92c2a1f7 100644
--- a/Modules/_pickle.c
+++ b/Modules/_pickle.c
@@ -5211,16 +5211,14 @@ load_int(PickleState *state, UnpicklerObject *self)
         return bad_readline(state);
 
     errno = 0;
-    /* XXX: Should the base argument of strtol() be explicitly set to 10?
-       XXX(avassalotti): Should this uses PyOS_strtol()? */
-    x = strtol(s, &endptr, 0);
+    /* XXX(avassalotti): Should this uses PyOS_strtol()? */
+    x = strtol(s, &endptr, 10);
 
     if (errno || (*endptr != '\n' && *endptr != '\0')) {
         /* Hm, maybe we've got something long.  Let's try reading
          * it as a Python int object. */
         errno = 0;
-        /* XXX: Same thing about the base here. */
-        value = PyLong_FromString(s, NULL, 0);
+        value = PyLong_FromString(s, NULL, 10);
         if (value == NULL) {
             PyErr_SetString(PyExc_ValueError,
                             "could not convert string to int");
@@ -5370,8 +5368,7 @@ load_long(PickleState *state, UnpicklerObject *self)
        the 'L' to be present. */
     if (s[len-2] == 'L')
         s[len-2] = '\0';
-    /* XXX: Should the base argument explicitly set to 10? */
-    value = PyLong_FromString(s, NULL, 0);
+    value = PyLong_FromString(s, NULL, 10);
     if (value == NULL)
         return -1;
 

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to