Skip to content

Fix quadratic-time backtracking in attr_list attribute regex - #1656

Open
naaz234 wants to merge 3 commits into
Python-Markdown:masterfrom
naaz234:attr-list-quadratic-backtracking
Open

naaz234 wants to merge 3 commits into
Python-Markdown:masterfrom
naaz234:attr-list-quadratic-backtracking

Conversation

@naaz234

@naaz234 naaz234 commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Description

AttrListTreeprocessor.BASE_RE in the attr_list extension is \{\:?[ ]*([^\}\n ][^\n]*)[ ]*\}. The capture group [^\n]* already eats spaces, and the [ ]* after it eats spaces too, so a run of trailing spaces can be split between the two pieces in n+1 ways. That's harmless when a } is found, but when a block ends with an unclosed { followed by lots of spaces there is no }, and the engine tries every split before giving up. The time grows quadratically with the number of spaces.

I noticed this while reading the regex, and confirmed it with a timing run against markdown.markdown(text, extensions=['attr_list']) on 'text\n{a' + ' ' * n:

trailing spaces before after
25,000 300 ms 3 ms
50,000 1,213 ms 6 ms
100,000 3,565 ms 11 ms
200,000 10,680 ms 22 ms

Doubling the spaces roughly quadruples the time before the change, and roughly doubles it after.

The fix is to drop the trailing [ ]*. It can never match anything in a successful match anyway: the greedy [^\n]* already consumes the trailing spaces and only gives back what \} needs, so the [ ]* is always empty. Removing it leaves every match and capture group the same (I checked old vs new patterns against the existing attr_list test cases, valid attribute lists, and a few thousand random brace/space/tab strings) and takes away the ambiguity, so the failing case is now linear. This affects HEADER_RE, BLOCK_RE and INLINE_RE, which are all built from BASE_RE.

It's the same sort of problem as the ReferenceProcessor fix in #798. Added a regression test with a 50k-space input and a changelog entry.

AI Assistance Disclosure

  • No AI tools were used in preparing this PR.
  • If AI tools were used, I have disclosed which ones, and fully reviewed and verified their output.

Checklist

`AttrListTreeprocessor.BASE_RE` matched the attribute content with
`([^\}\n ][^\n]*)` and then allowed trailing spaces before the closing
brace with a separate `[ ]*`. Because `[^\n]*` already matches spaces,
the two groups both cover the same run of trailing spaces. When a block
ends with an unclosed attribute list, the closing brace is never found,
so for a run of n spaces the engine retries the match from each of the
n+1 ways to split the spaces between the two groups, which is O(n^2).

The trailing `[ ]*` can only ever match zero characters in a successful
match (the greedy `[^\n]*` consumes the spaces first), so dropping it
leaves every match and capture group unchanged while removing the
ambiguity. A two-line block such as `text\n{a` followed by 200k spaces
drops from ~10s to ~20ms, and the cost is now linear in the input size.
@waylan

waylan commented Oct 14, 2026

Copy link
Copy Markdown
Member

Please review our AI Policy. Specifically, the following:

Contributors must be able to explain all changes upon request during review.

... The use of an AI agent to submit a pull request, issue, or comment autonomously is not permitted.

You description reads like it was written by AI. Also, as the PR template was deleted, I am assuming you had AI autonomously submit the PR. This presents 3 problems:

  1. Because you didn't write the description yourself, we don't know if you are able to explain the changes.
  2. Because the PR template was deleted, you did not disclose AI use.
  3. Having AI submit the PR is expressly forbidden and while this is a simple change to the code which is relatively easy to review, the policy requires that we close the request.

That said, it is possible that this was not AI generated. If you can honestly confirm that, then we will proceed. Otherwise, this will be closed.

PS to other maintainers: this is going to be my standard response to AI submitted PRs/issues from now on. Feel free to respond in kind if you see a new PR first.

@naaz234

naaz234 commented Oct 15, 2026

Copy link
Copy Markdown
Contributor Author

Hi @waylan, I apologize for this, I used AI for the wordings and stuff and pasted it without noticing.
Since I have gone through the change myself:The regex has two pieces in a row that can both eat spaces. When there’s a { with lots of spaces after it and no closing }, the regex engine tries every possible way of dividing the spaces between those two pieces before it gives up, and it repeats that from many starting points. That’s why it gets so slow. The second piece, [ ]*, never actually does anything useful, because the first piece already grabs all the spaces. Removing it leaves only one way to match, so the engine gives up quickly.

If the policy means this has to be closed, I understand. I’m happy to resubmit with the template filled in and the AI use disclosed, or to open an issue instead so a maintainer can make the fix.

@waylan

waylan commented Oct 16, 2026

Copy link
Copy Markdown
Member

It is fine of you provide thevinfo requested in the template.

left as plain text, no matter how many trailing spaces there are.

The content and the trailing spaces both match the same part of the
pattern, so without care the regex backtracks quadratically over the

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spellchecker does not recognize "quadratically" as a valid word. Either the comment needs to be rewritten to not use the word, or the word should be added to the dictionary at .spell-dict.

@waylan waylan added the requires-changes Awaiting updates after a review. label Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

requires-changes Awaiting updates after a review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants