[3.12] gh-149231: tomllib: Limit the number of parts in a key (GH-149233) (GH-149815) (#154143)

Yhg1s <[email protected]> Tue, 04 Aug 2026 05:31:47 -0400 (EDT)
Newsgroups gmane.comp.python.cvs
Message-ID <[email protected]>
https://github.com/python/cpython/commit/d7810660d543ba673fdf067b06922e0b88a5c603
commit: d7810660d543ba673fdf067b06922e0b88a5c603
branch: 3.12
author: Petr Viktorin <[email protected]>
committer: Yhg1s <[email protected]>
date: 2026-08-04T11:30:11+02:00
summary:

[3.12] gh-149231: tomllib: Limit the number of parts in a key (GH-149233) (GH-149815) (#154143)

[3.13] gh-149231: tomllib: Limit the number of parts in a key (GH-149233) (GH-149815)

(cherry picked from commit bc7c102f3462a9f014f3ac2546acfb471b2a7eae)
(cherry picked from commit 724a5e5e3b4ab3e943992193d79f83429d56d6d8)

Co-authored-by: Stan Ulbrych <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-05-01-16-45-31.gh-issue-149231.x2nBEE.rst
M Lib/test/test_tomllib/test_misc.py
M Lib/tomllib/_parser.py

diff --git a/Lib/test/test_tomllib/test_misc.py b/Lib/test/test_tomllib/test_misc.py
index 9e677a337a2835f..e6c9155d1a930e9 100644
--- a/Lib/test/test_tomllib/test_misc.py
+++ b/Lib/test/test_tomllib/test_misc.py
@@ -113,3 +113,16 @@ def test_inline_table_recursion_limit(self):
                               nest_count=nest_count):
                 recursive_table_toml = nest_count * "key = {" + nest_count * "}"
                 tomllib.loads(recursive_table_toml)
+
+    def test_key_recursion_limit(self):
+        nest_count = tomllib._parser.MAX_KEY_PARTS - 2
+        nested_key_toml = "a." * nest_count + "a = 1"
+        tomllib.loads(nested_key_toml)
+
+        nest_count = tomllib._parser.MAX_KEY_PARTS + 2
+        nested_key_toml = "a." * nest_count + "a = 1"
+        with self.assertRaisesRegex(
+            RecursionError,
+            r"TOML key has more than the allowed [0-9]+ parts",
+        ):
+            tomllib.loads(nested_key_toml)
diff --git a/Lib/tomllib/_parser.py b/Lib/tomllib/_parser.py
index 45ca7a89630f0e2..710ad10c0242700 100644
--- a/Lib/tomllib/_parser.py
+++ b/Lib/tomllib/_parser.py
@@ -6,6 +6,7 @@
 
 from collections.abc import Iterable
 import string
+import sys
 from types import MappingProxyType
 from typing import Any, BinaryIO, NamedTuple
 
@@ -19,6 +20,13 @@
 )
 from ._types import Key, ParseFloat, Pos
 
+# Pathologically excessive number of parts in a key runs into quadratic
+# behavior (e.g. in Flags.is_).
+# Even if keys aren't currently parsed using recursion, they name a
+# recursive structure, so it makes sense to limit it using getrecursionlimit()
+# and RecursionError.
+MAX_KEY_PARTS = sys.getrecursionlimit()
+
 ASCII_CTRL = frozenset(chr(i) for i in range(32)) | frozenset(chr(127))
 
 # Neither of these sets include quotation mark or backslash. They are
@@ -385,6 +393,10 @@ def parse_key(src: str, pos: Pos) -> tuple[Pos, Key]:
         pos = skip_chars(src, pos, TOML_WS)
         pos, key_part = parse_key_part(src, pos)
         key += (key_part,)
+        if len(key) > MAX_KEY_PARTS:
+            raise RecursionError(
+                f"TOML key has more than the allowed {MAX_KEY_PARTS} parts"
+            )
         pos = skip_chars(src, pos, TOML_WS)
 
 
diff --git a/Misc/NEWS.d/next/Library/2026-05-01-16-45-31.gh-issue-149231.x2nBEE.rst b/Misc/NEWS.d/next/Library/2026-05-01-16-45-31.gh-issue-149231.x2nBEE.rst
new file mode 100644
index 000000000000000..c265b54db8bed47
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-05-01-16-45-31.gh-issue-149231.x2nBEE.rst
@@ -0,0 +1 @@
+In :mod:`tomllib`, the number of parts in TOML keys is now limited.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]