Skip to content

Commit

Permalink
pythongh-126992: Change pickle code to base 10 for load_long and load…
Browse files Browse the repository at this point in the history
…_int (pythonGH-127042)

Co-authored-by: Serhiy Storchaka <[email protected]>
  • Loading branch information
Legoclones and serhiy-storchaka authored Dec 11, 2024
1 parent d5d84c3 commit ce76b54
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 9 deletions.
4 changes: 2 additions & 2 deletions Lib/pickle.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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):
Expand Down
20 changes: 20 additions & 0 deletions Lib/test/pickletester.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
37 changes: 37 additions & 0 deletions Lib/test/test_pickletools.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix LONG and INT opcodes to only use base 10 for string to integer conversion in :mod:`pickle`.
11 changes: 4 additions & 7 deletions Modules/_pickle.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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;

Expand Down

0 comments on commit ce76b54

Please sign in to comment.