Hello,

org-block-map signals "Stack overflow in regexp matcher" when matching a large
block. I can reproduce this with emacs -Q and without my personal configuration.

After loading Org, evaluate:

(with-temp-buffer
  (insert "#+begin_example\n"
          (make-string 1000000 ?x)
          "\n#+end_example\n")
  (let ((count 0))
    (org-block-map (lambda () (setq count (1+ count))))
    count))

Expected result: 1, with the callback called once.
Actual result: an error, "Stack overflow in regexp matcher".

The relevant call chain is:

  org-block-map
  org-at-block-p
  looking-at, using org-block-regexp

Emacs  : GNU Emacs 31.1 (build 2, x86_64-pc-linux-gnu, X toolkit, cairo version 
1.18.4, Xaw3d scroll bars)
Package: Org mode version 9.8.11 (release_9.8.11-3-g85490d)

The problem appears to be the repeated alternation in the block-body matcher.
Matching ordinary text through the non-final alternative allows backtracking
state to accumulate until the regexp stack overflows.

The attached patch replaces the repeated alternation used to match block
bodies in org-block-regexp with the equivalent [^z-a] character class.

This removes the alternation that grows the regexp backtracking stack while
matching large blocks. [^z-a] matches every character, including newline and
NUL; it is also the expansion of (rx anychar). Thus, it preserves the existing
semantics, including the NUL handling introduced by commit eaf274909.

This also follows advice in: 
https://www.gnu.org/software/emacs/manual/html_node/elisp/Regexp-Problems.html

I verified that org-block-map now succeeds with block bodies containing either
1,000,000 ordinary characters or 1,000,000 newlines. A block containing NUL
also still matches correctly. make compile passes.

Thanks,
Renato Ferreira

>From e4eff9db45f519ae6cacff108e27bedf9c9584e3 Mon Sep 17 00:00:00 2001
From: Renato Ferreira <[email protected]>
Date: Mon, 26 Oct 2026 13:25:33 -0300
Subject: [PATCH] lisp/org.el: Avoid stack overflow in large blocks

* lisp/org.el (org-block-regexp): Match block bodies with a bracket
expression instead of repeated alternation.  This avoids growing the
regexp backtracking stack while matching large block bodies.

TINYCHANGE
---
 lisp/org.el | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lisp/org.el b/lisp/org.el
index d48731b..d61246e 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -422,7 +422,7 @@ FULL is given."
 ;;;; Block
 
 (defconst org-block-regexp
-  "^[ \t]*#\\+begin_?\\([^ \n]+\\)\\(\\([^\n]+\\)\\)?\n\\(\\(?:.\\|\n\\)+?\\)#\\+end_?\\1[ \t]*$"
+  "^[ \t]*#\\+begin_?\\([^ \n]+\\)\\(\\([^\n]+\\)\\)?\n\\([^z-a]+?\\)#\\+end_?\\1[ \t]*$"
   "Regular expression for hiding blocks.")
 
 (defconst org-dblock-start-re
-- 
2.56.0

Reply via email to