Headline
CVE-2023-26479: Parsing deeply nested syntax causes a StackOverflowError
XWiki Platform is a generic wiki platform. Starting in version 6.0, users with write rights can insert well-formed content that is not handled well by the parser. As a consequence, some pages becomes unusable, including the user index (if the page containing the faulty content is a user page) and the page index. Note that on the page, the normal UI is completely missing and it is not possible to open the editor directly to revert the change as the stack overflow is already triggered while getting the title of the document. This means that it is quite difficult to remove this content once inserted. This has been patched in XWiki 13.10.10, 14.4.6, and 14.9-rc-1. A temporary workaround to avoid Stack Overflow errors is to increase the memory allocated to the stack by using the -Xss
JVM parameter (e.g., -Xss32m
). This should allow the parser to pass and to fix the faulty content. The consequences for other aspects of the system (e.g., performance) are unknown, and this workaround should be only be used as a temporary solution. The workaround does not prevent the issue occurring again with other content. Consequently, it is strongly advised to upgrade to a version where the issue has been patched.
Steps to reproduce:
- Create and login with an unprivileged account (no edit or script rights necessary)
- Edit the user profile with the wiki editor and change its content to 32768 lines with "(((", a line “Hello” and 32768 lines with ")))". You can create the string in the browser’s console using:
{ let result = "(((\nHello\n)))"; for (let i = 0; i < 15; ++i) { result = result.replace("Hello", result); } console.log(result); }
and then copy it by right-clicking on the result and copying the object. Save the user profile.
- Open the user profile
Expected result
The user profile is displayed and all other features of the wiki still work.
Actual result
The user profile isn’t displayed, instead a notice “A problem occurred while trying to process your request. Please contact the webmaster if this happens again.” with a long stack trace is displayed, here truncated, the last two lines are repeated many more times:
Detailed information:
Error number 0 in 11: Uncaught exception
com.xpn.xwiki.XWikiException: Error number 0 in 11: Uncaught exception at com.xpn.xwiki.web.XWikiAction.execute(XWikiAction.java:612) at com.xpn.xwiki.web.XWikiAction.execute(XWikiAction.java:289) at com.xpn.xwiki.web.LegacyActionServlet.service(LegacyActionServlet.java:114) at javax.servlet.http.HttpServlet.service(HttpServlet.java:590) at org.eclipse.jetty.servlet.ServletHolder$NotAsync.service(ServletHolder.java:1419) at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:764) at org.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1624) at com.xpn.xwiki.web.ActionFilter.doFilter(ActionFilter.java:122) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594) at org.xwiki.wysiwyg.filter.ConversionFilter.doFilter(ConversionFilter.java:61) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594) at org.xwiki.container.servlet.filters.internal.SetHTTPHeaderFilter.doFilter(SetHTTPHeaderFilter.java:63) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594) at org.xwiki.container.servlet.filters.internal.SavedRequestRestorerFilter.doFilter(SavedRequestRestorerFilter.java:208) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594) at org.xwiki.container.servlet.filters.internal.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:111) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594) at org.xwiki.resource.servlet.RoutingFilter.doFilter(RoutingFilter.java:132) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:210) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594) at org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:164) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594) at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:506) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:131) at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:578) at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122) at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:223) at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1571) at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221) at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1378) at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176) at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:463) at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1544) at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174) at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1300) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129) at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:192) at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:51) at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122) at org.eclipse.jetty.server.Server.handle(Server.java:562) at org.eclipse.jetty.server.HttpChannel.lambda$handle$0(HttpChannel.java:418) at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:675) at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:410) at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:282) at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:319) at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100) at org.eclipse.jetty.io.SocketChannelEndPoint$1.run(SocketChannelEndPoint.java:101) at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:412) at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:381) at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:268) at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.lambda$new$0(AdaptiveExecutionStrategy.java:138) at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:407) at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:894) at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1038) at java.base/java.lang.Thread.run(Thread.java:829) Caused by: java.lang.StackOverflowError at org.xwiki.rendering.internal.parser.wikimodel.DefaultXWikiGeneratorListener.beginDocument(DefaultXWikiGeneratorListener.java:419) at org.xwiki.rendering.wikimodel.impl.WikiScannerContext$DefaultSectionListener.beginDocument(WikiScannerContext.java:50) at org.xwiki.rendering.wikimodel.util.SectionBuilder$1.onBeginTree(SectionBuilder.java:114) at org.xwiki.rendering.wikimodel.util.SectionBuilder$1.onBeginTree(SectionBuilder.java:98) at org.xwiki.rendering.wikimodel.util.TreeBuilder.addTail(TreeBuilder.java:76) at org.xwiki.rendering.wikimodel.util.TreeBuilder.doAlign(TreeBuilder.java:111) at org.xwiki.rendering.wikimodel.util.TreeBuilder.align(TreeBuilder.java:153) at org.xwiki.rendering.wikimodel.util.SectionBuilder.align(SectionBuilder.java:148) at org.xwiki.rendering.wikimodel.util.SectionBuilder.beginDocument(SectionBuilder.java:157) at org.xwiki.rendering.wikimodel.impl.InternalWikiScannerContext.beginDocument(InternalWikiScannerContext.java:149) at org.xwiki.rendering.wikimodel.impl.WikiScannerContext.beginDocument(WikiScannerContext.java:129) at org.xwiki.rendering.wikimodel.internal.xwiki.xwiki21.javacc.XWikiScanner.embeddedDocument(XWikiScanner.java:1270) at org.xwiki.rendering.wikimodel.internal.xwiki.xwiki21.javacc.XWikiScanner.docElements(XWikiScanner.java:319) at org.xwiki.rendering.wikimodel.internal.xwiki.xwiki21.javacc.XWikiScanner.embeddedDocument(XWikiScanner.java:1319) at org.xwiki.rendering.wikimodel.internal.xwiki.xwiki21.javacc.XWikiScanner.docElements(XWikiScanner.java:319) at org.xwiki.rendering.wikimodel.internal.xwiki.xwiki21.javacc.XWikiScanner.embeddedDocument(XWikiScanner.java:1319) at org.xwiki.rendering.wikimodel.internal.xwiki.xwiki21.javacc.XWikiScanner.docElements(XWikiScanner.java:319) at org.xwiki.rendering.wikimodel.internal.xwiki.xwiki21.javacc.XWikiScanner.embeddedDocument(XWikiScanner.java:1319) at org.xwiki.rendering.wikimodel.internal.xwiki.xwiki21.javacc.XWikiScanner.docElements(XWikiScanner.java:319) at org.xwiki.rendering.wikimodel.internal.xwiki.xwiki21.javacc.XWikiScanner.embeddedDocument(XWikiScanner.java:1319) at org.xwiki.rendering.wikimodel.internal.xwiki.xwiki21.javacc.XWikiScanner.docElements(XWikiScanner.java:319) at org.xwiki.rendering.wikimodel.internal.xwiki.xwiki21.javacc.XWikiScanner.embeddedDocument(XWikiScanner.java:1319) at org.xwiki.rendering.wikimodel.internal.xwiki.xwiki21.javacc.XWikiScanner.docElements(XWikiScanner.java:319) at org.xwiki.rendering.wikimodel.internal.xwiki.xwiki21.javacc.XWikiScanner.embeddedDocument(XWikiScanner.java:1319) at org.xwiki.rendering.wikimodel.internal.xwiki.xwiki21.javacc.XWikiScanner.docElements(XWikiScanner.java:319) at org.xwiki.rendering.wikimodel.internal.xwiki.xwiki21.javacc.XWikiScanner.embeddedDocument(XWikiScanner.java:1319)
Also, all other places that would display the user’s name are broken, this includes the user index and the page index if the user would be included in the list. Note that on the page with the syntax the normal UI is completely missing and it is not possible to open the editor directly to revert the change as the stack overflow is already triggered while getting the title of the document. This means that it is quite difficult to remove this content again, this is probably also an issue in comments.
The cause of this issue is that the parser is recursive and with this syntax, we’re hitting a java.lang.StackOverflowError due to this. The affects version is just the version where I reproduced the problem, this is most likely as old as the use of WikiModel or even older (not sure if there are other places where recursion would also cause a stack overflow).
I think a fix would be to introduce a nesting limit that is enforced in WikiModel but also somewhere in the XDOM rendering to avoid the stack overflow, or alternatively, to re-write the rendering code to be non-recursive, but this seems much harder. Apart from that, rendering-calls in XWiki platform should be protected such that an exception during rendering doesn’t fail the whole UI (e.g., just use an error message as rendered content if rendering fails).
Related news
### Impact Users with write rights can insert well-formed content that is not handled well by the parser. For instance, with `xwiki/2.1`, inserting a deeply nested group blocks (`((( ((( ((( ((( .... ))) ))) ))) )))` (see the generator below to produce a large payload) can lead to the parser throwing a `StackOverflowError`. As a consequence, some pages becomes unusable, including: - the user index (if the page containing the faulty content is a user page) - the page index Note that on the page, the normal UI is completely missing and it is not possible to open the editor directly to revert the change as the stack overflow is already triggered while getting the title of the document. This means that it is quite difficult to remove this content once inserted. ### Patches This has been patched on XWiki 13.10.10, 14.4.6, and 14.9-rc-1. ### Workarounds A temporary solution to avoid Stack Overflow errors is to increase the memory allocated to the stack by using the `-Xss` JVM parameter (e...