Skip to content

Commit b274f1c

Browse files
authored
bpo-32962: Fix test_gdb failure in debug build with -mcet -fcf-protection -O0 (GH-9656) (GH-9788)
When Python is built with the intel control-flow protection flags, -mcet -fcf-protection, gdb is not able to read the stack without actually jumping inside the function. This means an extra 'next' command is required to make the $pc (program counter) enter the function and make the stack of the function exposed to gdb. test_gdb: get_gdb_repr() now uses the "backtrace 1" command after breakpoint, as in the master branch. Co-Authored-By: Marcel Plch <[email protected]> (cherry picked from commit 9b7c74c) (cherry picked from commit 79d2133)
1 parent 4a7dd30 commit b274f1c

File tree

2 files changed

+38
-1
lines changed

2 files changed

+38
-1
lines changed

Lib/test/test_gdb.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,23 @@ def get_gdb_version():
5858
checkout_hook_path = os.path.join(os.path.dirname(sys.executable),
5959
'python-gdb.py')
6060

61+
62+
def cet_protection():
63+
cflags = sysconfig.get_config_var('CFLAGS')
64+
if not cflags:
65+
return False
66+
flags = cflags.split()
67+
# True if "-mcet -fcf-protection" options are found, but false
68+
# if "-fcf-protection=none" or "-fcf-protection=return" is found.
69+
return (('-mcet' in flags)
70+
and any((flag.startswith('-fcf-protection')
71+
and not flag.endswith(("=none", "=return")))
72+
for flag in flags))
73+
74+
# Control-flow enforcement technology
75+
CET_PROTECTION = cet_protection()
76+
77+
6178
def run_gdb(*args, **env_vars):
6279
"""Runs gdb in batch mode with the additional arguments given by *args.
6380
@@ -171,6 +188,12 @@ def get_stack_trace(self, source=None, script=None,
171188
commands += ['set print entry-values no']
172189

173190
if cmds_after_breakpoint:
191+
if CET_PROTECTION:
192+
# bpo-32962: When Python is compiled with -mcet
193+
# -fcf-protection, function arguments are unusable before
194+
# running the first instruction of the function entry point.
195+
# The 'next' command makes the required first step.
196+
commands += ['next']
174197
commands += cmds_after_breakpoint
175198
else:
176199
commands += ['backtrace']
@@ -241,6 +264,11 @@ def get_gdb_repr(self, source,
241264
#
242265
# For a nested structure, the first time we hit the breakpoint will
243266
# give us the top-level structure
267+
268+
# NOTE: avoid decoding too much of the traceback as some
269+
# undecodable characters may lurk there in optimized mode
270+
# (issue #19743).
271+
cmds_after_breakpoint = cmds_after_breakpoint or ["backtrace 1"]
244272
gdb_output = self.get_stack_trace(source, breakpoint='PyObject_Print',
245273
cmds_after_breakpoint=cmds_after_breakpoint,
246274
import_site=import_site)
@@ -886,9 +914,17 @@ def __init__(self):
886914
print("first break point")
887915
l = MyList()
888916
''')
917+
cmds_after_breakpoint = ['break wrapper_call', 'continue']
918+
if CET_PROTECTION:
919+
# bpo-32962: same case as in get_stack_trace():
920+
# we need an additional 'next' command in order to read
921+
# arguments of the innermost function of the call stack.
922+
cmds_after_breakpoint.append('next')
923+
cmds_after_breakpoint.append('py-bt')
924+
889925
# Verify with "py-bt":
890926
gdb_output = self.get_stack_trace(cmd,
891-
cmds_after_breakpoint=['break wrapper_call', 'continue', 'py-bt'])
927+
cmds_after_breakpoint=cmds_after_breakpoint)
892928
self.assertRegexpMatches(gdb_output,
893929
r"<method-wrapper u?'__init__' of MyList object at ")
894930

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed test_gdb when Python is compiled with flags -mcet -fcf-protection -O0.

0 commit comments

Comments
 (0)