Uploaded image for project: 'PUBLIC - Liferay Portal Community Edition'
  1. PUBLIC - Liferay Portal Community Edition
  2. LPS-148916

Regex in AMBackwardsCompatibilityHtmlContentTransformer.java has exponential complexity

Details

    Description

      Let's have a web content template that has an image tag in it.

      One part of the template looks as follows:

      <#if (cta.Picto.getData())?? && cta.Picto.getData() != "">
                  <img src="${cta.Picto.getData()}" alt="${cta.Picto.getAttribute("alt")}"
                       data-fileentryid="${cta.Picto.getAttribute("fileEntryId")}"
                       class="Right-sticky-menu__image"/>
                </#if>
      

      which when transformed will look like this:

      <div>
       <img alt="toto" 
        src="/documents/20121/0/den7488-1557c026-fc2a-450e-b9be-13e01051a544.jpg/a4dc4ae0-0cb9-954d-fa3c-a4db878f3c1b?t=1646727182374"
        data-fileentryid="79510"
        class="Right-sticky-menu__image"
       />
      </div>
      

      On rendering the content the portal wants to perform a transform using com.liferay.adaptive.media.image.content.transformer.backwards.compatibility.internal.AMBackwardsCompatibilityHtmlContentTransformer.transform

      with the regex declared in
      com.liferay.adaptive.media.image.content.transformer.backwards.compatibility.internal.AMBackwardsCompatibilityHtmlContentTransformer.java
      (https://github.com/liferay/liferay-portal-ee/blob/7.3.x/modules%2Fapps%2Fadaptive-media%2Fadaptive-media-image-content-transformer-backwards-compatibility%2Fsrc%2Fmain%2Fjava%2Fcom%2Fliferay%2Fadaptive%2Fmedia%2Fimage%2Fcontent%2Ftransformer%2Fbackwards%2Fcompatibility%2Finternal%2FAMBackwardsCompatibilityHtmlContentTransformer.java#L106)

      This regex however has an exponential algorithmic complexity:

      private static final Pattern _pattern = Pattern.compile(
      		"<img\\s+(?:[^>]*\\s)*src=['\"](?:/?[^\\s]*)/documents/(\\d+)/(\\d+)" +
      			"/([^/?]+)(?:/([-0-9a-fA-F]+))?(?:\\?t=\\d+)?['\"][^>]*/>");
      

      The problem is that just after <img , we have that optional group that matches every character which is not a >.
      As a consequence, the whole contents of the img tag will be matched until it fails to recognize the next elements of the regex. Then, the regex will recursively backtrack one character and try again.

      Thus in case of long image tags, the number of steps needed to perform for the matcher explodes. The image tag above needs like 60 000 steps. As a consequence the CPU gets in an infinite loop, and the content never renders:

      "http-nio-8080-exec-1" #132 daemon prio=5 os_prio=0 tid=0x00007f54202c8800 nid=0x14d runnable [0x00007f545a69c000]
         java.lang.Thread.State: RUNNABLE
      	at java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3811)
      	at java.util.regex.Pattern$Curly.match0(Pattern.java:4286)
      	at java.util.regex.Pattern$Curly.match(Pattern.java:4248)
      	at java.util.regex.Pattern$GroupHead.match(Pattern.java:4672)
      	at java.util.regex.Pattern$Loop.match(Pattern.java:4799)
      	at java.util.regex.Pattern$GroupTail.match(Pattern.java:4731)
      	at java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3812)
      	at java.util.regex.Pattern$Curly.match0(Pattern.java:4286)
      	at java.util.regex.Pattern$Curly.match(Pattern.java:4248)
      	at java.util.regex.Pattern$GroupHead.match(Pattern.java:4672)
      	at java.util.regex.Pattern$Loop.match(Pattern.java:4799)
      	at java.util.regex.Pattern$GroupTail.match(Pattern.java:4731)
      	at java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3812)
      	at java.util.regex.Pattern$Curly.match0(Pattern.java:4286)
      	at java.util.regex.Pattern$Curly.match(Pattern.java:4248)
      	at java.util.regex.Pattern$GroupHead.match(Pattern.java:4672)
      	at java.util.regex.Pattern$Loop.match(Pattern.java:4799)
      	at java.util.regex.Pattern$GroupTail.match(Pattern.java:4731)
      	at java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3812)
      	at java.util.regex.Pattern$Curly.match0(Pattern.java:4286)
      	at java.util.regex.Pattern$Curly.match(Pattern.java:4248)
      	at java.util.regex.Pattern$GroupHead.match(Pattern.java:4672)
      	at java.util.regex.Pattern$Loop.match(Pattern.java:4799)
      	at java.util.regex.Pattern$GroupTail.match(Pattern.java:4731)
      	at java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3812)
      	at java.util.regex.Pattern$Curly.match0(Pattern.java:4286)
      	at java.util.regex.Pattern$Curly.match(Pattern.java:4248)
      	at java.util.regex.Pattern$GroupHead.match(Pattern.java:4672)
      	at java.util.regex.Pattern$Loop.match(Pattern.java:4799)
      	at java.util.regex.Pattern$GroupTail.match(Pattern.java:4731)
      	at java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3812)
      	at java.util.regex.Pattern$Curly.match0(Pattern.java:4286)
      	at java.util.regex.Pattern$Curly.match(Pattern.java:4248)
      	at java.util.regex.Pattern$GroupHead.match(Pattern.java:4672)
      	at java.util.regex.Pattern$Loop.match(Pattern.java:4799)
      	at java.util.regex.Pattern$GroupTail.match(Pattern.java:4731)
      	at java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3812)
      	at java.util.regex.Pattern$Curly.match0(Pattern.java:4286)
      	at java.util.regex.Pattern$Curly.match(Pattern.java:4248)
      	at java.util.regex.Pattern$GroupHead.match(Pattern.java:4672)
      	at java.util.regex.Pattern$Loop.match(Pattern.java:4799)
      	at java.util.regex.Pattern$GroupTail.match(Pattern.java:4731)
      	at java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3812)
      	at java.util.regex.Pattern$Curly.match0(Pattern.java:4286)
      	at java.util.regex.Pattern$Curly.match(Pattern.java:4248)
      	at java.util.regex.Pattern$GroupHead.match(Pattern.java:4672)
      	at java.util.regex.Pattern$Loop.match(Pattern.java:4799)
      	at java.util.regex.Pattern$GroupTail.match(Pattern.java:4731)
      	at java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3812)
      	at java.util.regex.Pattern$Curly.match0(Pattern.java:4286)
      	at java.util.regex.Pattern$Curly.match(Pattern.java:4248)
      	at java.util.regex.Pattern$GroupHead.match(Pattern.java:4672)
      	at java.util.regex.Pattern$Loop.match(Pattern.java:4799)
      	at java.util.regex.Pattern$GroupTail.match(Pattern.java:4731)
      	at java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3812)
      	at java.util.regex.Pattern$Curly.match0(Pattern.java:4286)
      	at java.util.regex.Pattern$Curly.match(Pattern.java:4248)
      	at java.util.regex.Pattern$GroupHead.match(Pattern.java:4672)
      	at java.util.regex.Pattern$Loop.match(Pattern.java:4799)
      	at java.util.regex.Pattern$GroupTail.match(Pattern.java:4731)
      	at java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3812)
      	at java.util.regex.Pattern$Curly.match0(Pattern.java:4286)
      	at java.util.regex.Pattern$Curly.match(Pattern.java:4248)
      	at java.util.regex.Pattern$GroupHead.match(Pattern.java:4672)
      	at java.util.regex.Pattern$Loop.match(Pattern.java:4799)
      	at java.util.regex.Pattern$GroupTail.match(Pattern.java:4731)
      	at java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3812)
      	at java.util.regex.Pattern$Curly.match0(Pattern.java:4286)
      	at java.util.regex.Pattern$Curly.match(Pattern.java:4248)
      	at java.util.regex.Pattern$GroupHead.match(Pattern.java:4672)
      	at java.util.regex.Pattern$Loop.matchInit(Pattern.java:4818)
      	at java.util.regex.Pattern$Prolog.match(Pattern.java:4755)
      	at java.util.regex.Pattern$Curly.match0(Pattern.java:4293)
      	at java.util.regex.Pattern$Curly.match(Pattern.java:4248)
      	at java.util.regex.Pattern$BnM.match(Pattern.java:5483)
      	at java.util.regex.Matcher.search(Matcher.java:1248)
      	at java.util.regex.Matcher.find(Matcher.java:637)
      	at com.liferay.adaptive.media.content.transformer.BaseRegexStringContentTransformer.transform(BaseRegexStringContentTransformer.java:46)
      	at com.liferay.adaptive.media.image.content.transformer.backwards.compatibility.internal.AMBackwardsCompatibilityHtmlContentTransformer.transform(AMBackwardsCompatibilityHtmlContentTransformer.java:60)
      	at com.liferay.adaptive.media.image.content.transformer.backwards.compatibility.internal.AMBackwardsCompatibilityHtmlContentTransformer.transform(AMBackwardsCompatibilityHtmlContentTransformer.java:36)
      	at com.liferay.adaptive.media.content.transformer.internal.ContentTransformerHandlerImpl.transform(ContentTransformerHandlerImpl.java:65)
      	at com.liferay.adaptive.media.journal.web.internal.transformer.AMJournalTransformerListener.onOutput(AMJournalTransformerListener.java:45)
      	at com.liferay.journal.internal.transformer.JournalTransformer.doTransform(JournalTransformer.java:401)
      	at com.liferay.journal.internal.transformer.JournalTransformer.transform(JournalTransformer.java:103)
      	at com.liferay.journal.internal.util.JournalUtil.transform(JournalUtil.java:424)
      	at com.liferay.journal.service.impl.JournalArticleLocalServiceImpl.getArticleDisplay(JournalArticleLocalServiceImpl.java:7796)
      

      To reproduce the issue:
      1. Create a new structure and use the source in right_sticky_menu.json
      2. Create a new template, set right-sticky-structure, and use the source in right_sticky_menu.tfl
      3. Create a new web content and fill the picto image field among the mandatory fields.
      4. Place the web content on a web content display portlet

      The webcontent never renders, cpu on 100%

      Attachments

        Issue Links

          Activity

            People

              yang.cao Yang Cao
              istvan.dezsi Istvan Dezsi
              Enterprise Release HU Enterprise Release HU
              Istvan Dezsi Istvan Dezsi
              Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:
                36 weeks, 3 days ago

                Packages

                  Version Package
                  7.2.X
                  7.3.X
                  7.4.3.18 CE GA18
                  7.4.3.19 CE GA19
                  Master