bpo-11105: Do not crash when compiling recursive ASTs (GH-20594)


When compiling an AST object with a direct / indirect reference
cycles, on the conversion phase because of exceeding amount of
calls, a segfault was raised. This patch adds recursion guards to
places for preventing user inputs to not to crash AST but instead
raise a RecursionError.
(cherry picked from commit f3491242e41933aa9529add7102edb68b80a25e9)

Co-authored-by: Batuhan Taskaya <batuhan@python.org>
diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py
index 80d24e9..e08a965 100644
--- a/Lib/test/test_ast.py
+++ b/Lib/test/test_ast.py
@@ -1097,6 +1097,20 @@ def test_level_as_none(self):
         exec(code, ns)
         self.assertIn('sleep', ns)
 
+    def test_recursion_direct(self):
+        e = ast.UnaryOp(op=ast.Not(), lineno=0, col_offset=0)
+        e.operand = e
+        with self.assertRaises(RecursionError):
+            compile(ast.Expression(e), "<test>", "eval")
+
+    def test_recursion_indirect(self):
+        e = ast.UnaryOp(op=ast.Not(), lineno=0, col_offset=0)
+        f = ast.UnaryOp(op=ast.Not(), lineno=0, col_offset=0)
+        e.operand = f
+        f.operand = e
+        with self.assertRaises(RecursionError):
+            compile(ast.Expression(e), "<test>", "eval")
+
 
 class ASTValidatorTests(unittest.TestCase):