Kaydet (Commit) 3634560f authored tarafından Vinay Karanam's avatar Vinay Karanam Kaydeden (comit) Tim Graham

Fixed #29393 -- Prevented infinite loop in ExceptionReporter.get_traceback_frames().

üst 199025fa
...@@ -397,6 +397,9 @@ class ExceptionReporter: ...@@ -397,6 +397,9 @@ class ExceptionReporter:
while exc_value: while exc_value:
exceptions.append(exc_value) exceptions.append(exc_value)
exc_value = explicit_or_implicit_cause(exc_value) exc_value = explicit_or_implicit_cause(exc_value)
if exc_value in exceptions:
# Avoid infinite loop if there's a cyclic reference (#29393).
break
frames = [] frames = []
# No exceptions were supplied to ExceptionReporter # No exceptions were supplied to ExceptionReporter
......
...@@ -4,6 +4,7 @@ import os ...@@ -4,6 +4,7 @@ import os
import re import re
import sys import sys
import tempfile import tempfile
import threading
from io import StringIO from io import StringIO
from pathlib import Path from pathlib import Path
...@@ -403,6 +404,44 @@ class ExceptionReporterTests(SimpleTestCase): ...@@ -403,6 +404,44 @@ class ExceptionReporterTests(SimpleTestCase):
text = reporter.get_traceback_text() text = reporter.get_traceback_text()
self.assertIn('"generated" in funcName', text) self.assertIn('"generated" in funcName', text)
def test_reporting_frames_for_cyclic_reference(self):
try:
def test_func():
try:
raise RuntimeError('outer') from RuntimeError('inner')
except RuntimeError as exc:
raise exc.__cause__
test_func()
except Exception:
exc_type, exc_value, tb = sys.exc_info()
request = self.rf.get('/test_view/')
reporter = ExceptionReporter(request, exc_type, exc_value, tb)
def generate_traceback_frames(*args, **kwargs):
nonlocal tb_frames
tb_frames = reporter.get_traceback_frames()
tb_frames = None
tb_generator = threading.Thread(target=generate_traceback_frames, daemon=True)
tb_generator.start()
tb_generator.join(timeout=5)
if tb_generator.is_alive():
# tb_generator is a daemon that runs until the main thread/process
# exits. This is resource heavy when running the full test suite.
# Setting the following values to None makes
# reporter.get_traceback_frames() exit early.
exc_value.__traceback__ = exc_value.__context__ = exc_value.__cause__ = None
tb_generator.join()
self.fail('Cyclic reference in Exception Reporter.get_traceback_frames()')
if tb_frames is None:
# can happen if the thread generating traceback got killed
# or exception while generating the traceback
self.fail('Traceback generation failed')
last_frame = tb_frames[-1]
self.assertIn('raise exc.__cause__', last_frame['context_line'])
self.assertEqual(last_frame['filename'], __file__)
self.assertEqual(last_frame['function'], 'test_func')
def test_request_and_message(self): def test_request_and_message(self):
"A message can be provided in addition to a request" "A message can be provided in addition to a request"
request = self.rf.get('/test_view/') request = self.rf.get('/test_view/')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment