diff --git a/Lib/dis.py b/Lib/dis.py index 2462a8434e8950..dc3ec169abecf0 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -515,6 +515,12 @@ def _disassemble_str(source, **kwargs): disco = disassemble # XXX For backwards compatibility + +# Rely on C `int` being 32 bits for oparg +_INT_BITS = 32 +# Value for c int when it overflows +_INT_OVERFLOW = 2 ** (_INT_BITS - 1) + def _unpack_opargs(code): extended_arg = 0 for i in range(0, len(code), 2): @@ -522,6 +528,11 @@ def _unpack_opargs(code): if op >= HAVE_ARGUMENT: arg = code[i+1] | extended_arg extended_arg = (arg << 8) if op == EXTENDED_ARG else 0 + # The oparg is stored as a signed integer + # If the value exceeds its upper limit, it will overflow and wrap + # to a negative integer + if extended_arg >= _INT_OVERFLOW: + extended_arg -= 2 * _INT_OVERFLOW else: arg = None extended_arg = 0 diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 4feeb8c5be8a25..488b8dffdedbf2 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -1,14 +1,17 @@ # Minimal tests for dis module -from test.support import captured_stdout, requires_debug_ranges -from test.support.bytecode_helper import BytecodeTestCase -import unittest -import sys +import contextlib import dis import io import re +import sys import types -import contextlib +import unittest +from test.support import captured_stdout, requires_debug_ranges +from test.support.bytecode_helper import BytecodeTestCase + +import opcode + def get_tb(): def _error(): @@ -219,6 +222,22 @@ def bug42562(): RETURN_VALUE """ +# [255, 255, 255, 252] is -4 in a 4 byte signed integer +bug46724 = bytes([ + opcode.EXTENDED_ARG, 255, + opcode.EXTENDED_ARG, 255, + opcode.EXTENDED_ARG, 255, + opcode.opmap['JUMP_FORWARD'], 252, +]) + + +dis_bug46724 = """\ + >> EXTENDED_ARG 255 + EXTENDED_ARG 65535 + EXTENDED_ARG 16777215 + JUMP_FORWARD -4 (to 0) +""" + _BIG_LINENO_FORMAT = """\ 1 RESUME 0 @@ -688,6 +707,10 @@ def test_bug_45757(self): # Extended arg followed by NOP self.do_disassembly_test(code_bug_45757, dis_bug_45757) + def test_bug_46724(self): + # Test that negative operargs are handled properly + self.do_disassembly_test(bug46724, dis_bug46724) + def test_big_linenos(self): def func(count): namespace = {} diff --git a/Misc/NEWS.d/next/Library/2022-02-11-20-41-17.bpo-46724.eU52_N.rst b/Misc/NEWS.d/next/Library/2022-02-11-20-41-17.bpo-46724.eU52_N.rst new file mode 100644 index 00000000000000..9ac8c17deb7a2d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-02-11-20-41-17.bpo-46724.eU52_N.rst @@ -0,0 +1 @@ +Fix :mod:`dis` behavior on negative jump offsets.