From 7a7ed86507bb8f90f2fe9670849a08b2184f0010 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Fri, 30 Jun 2017 14:51:36 +0200 Subject: [PATCH] Report SOE in regular expression find with baloon, avoid SOE for some patterns (IDEA-150928) --- .../java/find/impl/FindManagerTest.java | 25 +++++++++++++++++ .../src/com/intellij/find/FindModel.java | 14 +++++++++- .../intellij/find/impl/FindManagerImpl.java | 28 +++++++++++++++++-- platform/util/src/com/intellij/Patches.java | 3 ++ 4 files changed, 67 insertions(+), 3 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/java/find/impl/FindManagerTest.java b/java/java-tests/testSrc/com/intellij/java/find/impl/FindManagerTest.java index c1c91383a19e..53aceeb8feac 100644 --- a/java/java-tests/testSrc/com/intellij/java/find/impl/FindManagerTest.java +++ b/java/java-tests/testSrc/com/intellij/java/find/impl/FindManagerTest.java @@ -927,6 +927,31 @@ public class FindManagerTest extends DaemonAnalyzerTestCase { assertTrue(!findResult.isStringFound()); } + public void testRegExpSOEWhenMatch2() throws InterruptedException { + String text = "package com.intellij.demo;\n" + + "\n"; + for(int i = 0; i < 10; ++i) text += text; + text += "public class Foo {}"; + + FindModel findModel = FindManagerTestUtils.configureFindModel("package((?:\\n|.)+)class (\\w+)"); + findModel.setRegularExpressions(true); + + FindResult findResult = myFindManager.findString(text, 0, findModel, null); + assertTrue(findResult.isStringFound()); + + findModel = FindManagerTestUtils.configureFindModel("package((.|\\n)+)class (\\w+)"); + findModel.setRegularExpressions(true); + + findResult = myFindManager.findString(text, 0, findModel, null); + assertTrue(findResult.isStringFound()); + + findModel = FindManagerTestUtils.configureFindModel("package(([^\\n]|\\n)+)class (\\w+)"); + findModel.setRegularExpressions(true); + + findResult = myFindManager.findString(text, 0, findModel, null); + assertTrue(!findResult.isStringFound()); // SOE, no match + } + public void testFindRegexpThatMatchesWholeFile() throws Exception { FindModel findModel = FindManagerTestUtils.configureFindModel("[^~]+\\Z"); findModel.setRegularExpressions(true); diff --git a/platform/indexing-api/src/com/intellij/find/FindModel.java b/platform/indexing-api/src/com/intellij/find/FindModel.java index ae7ecaf8de56..0b5f1b57a225 100644 --- a/platform/indexing-api/src/com/intellij/find/FindModel.java +++ b/platform/indexing-api/src/com/intellij/find/FindModel.java @@ -15,6 +15,7 @@ */ package com.intellij.find; +import com.intellij.Patches; import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; @@ -937,8 +938,19 @@ public class FindModel extends UserDataHolderBase implements Cloneable { Pattern pattern = myPattern; if (pattern == PatternUtil.NOTHING) { + int flags = isCaseSensitive() ? Pattern.MULTILINE : Pattern.MULTILINE | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE; + + if (toFind.contains("\\n") && Patches.JDK_SOE_IN_REGEXP) { // if needed use DOT_ALL for modified pattern to avoid SOE + String modifiedStringToFind = StringUtil.replace(toFind, "\\n|.", "."); + modifiedStringToFind = StringUtil.replace(modifiedStringToFind, ".|\\n", "."); + + if (!modifiedStringToFind.equals(toFind)) { + flags |= Pattern.DOTALL; + toFind = modifiedStringToFind; + } + } try { - myPattern = pattern = Pattern.compile(toFind, isCaseSensitive() ? Pattern.MULTILINE : Pattern.MULTILINE | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); + myPattern = pattern = Pattern.compile(toFind, flags); } catch (PatternSyntaxException e) { myPattern = pattern = null; diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java b/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java index 70d80851feea..6ba3368bf78f 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java @@ -30,11 +30,15 @@ import com.intellij.lang.LanguageUtil; import com.intellij.lang.ParserDefinition; import com.intellij.lexer.Lexer; import com.intellij.navigation.NavigationItem; +import com.intellij.notification.NotificationDisplayType; +import com.intellij.notification.NotificationGroup; +import com.intellij.notification.NotificationType; import com.intellij.notification.impl.NotificationsConfigurationImpl; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.IdeActions; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.*; @@ -65,6 +69,7 @@ import com.intellij.ui.ReplacePromptDialog; import com.intellij.usages.ChunkExtractor; import com.intellij.usages.UsageViewManager; import com.intellij.usages.impl.SyntaxHighlighterOverEditorHighlighter; +import com.intellij.util.containers.ConcurrentIntObjectMap; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.Predicate; import com.intellij.util.messages.MessageBus; @@ -100,6 +105,7 @@ public class FindManagerImpl extends FindManager { private static final Key HIGHLIGHTER_WAS_NOT_FOUND_KEY = Key.create("com.intellij.find.impl.FindManagerImpl.HighlighterNotFoundKey"); private FindUIHelper myHelper; + private static final NotificationGroup GROUP = new NotificationGroup("Find Problems", NotificationDisplayType.STICKY_BALLOON, false); public FindManagerImpl(Project project, FindSettings findSettings, UsageViewManager anotherManager, MessageBus bus) { myProject = project; @@ -475,7 +481,7 @@ public class FindManagerImpl extends FindManager { } if (model.isRegularExpressions()){ - return findStringByRegularExpression(text, offset, model); + return findStringByRegularExpression(text, offset, model, file); } final StringSearcher searcher = createStringSearcher(model); @@ -730,7 +736,10 @@ public class FindManagerImpl extends FindManager { return syntaxHighlighter; } - private static FindResult findStringByRegularExpression(CharSequence text, int startOffset, FindModel model) { + private FindResult findStringByRegularExpression(CharSequence text, + int startOffset, + FindModel model, + VirtualFile file) { Matcher matcher = compileRegExp(model, text); if (matcher == null) { return NOT_FOUND_RESULT; @@ -757,9 +766,24 @@ public class FindManagerImpl extends FindManager { return new FindResultImpl(start, end); } } catch (StackOverflowError soe) { + String stringToFind = model.getStringToFind(); + + if (!ApplicationManager.getApplication().isHeadlessEnvironment() && + ourReportedPatterns.put(stringToFind.hashCode(), Boolean.TRUE) == null) { + String content = stringToFind + " produced stack overflow when matching content of the file"; + LOG.info(content); + //noinspection SSBasedInspection + GROUP.createNotification("Regular expression failed to match", + content + " " + file.getPath(), + NotificationType.ERROR, + null + ).notify(myProject); + } return NOT_FOUND_RESULT; } } + + private static final ConcurrentIntObjectMap ourReportedPatterns = ContainerUtil.createConcurrentIntObjectMap(); private static Matcher compileRegExp(FindModel model, CharSequence text) { Pattern pattern = model.compileRegExp(); diff --git a/platform/util/src/com/intellij/Patches.java b/platform/util/src/com/intellij/Patches.java index 75a632d4a426..f2ebbf66a724 100644 --- a/platform/util/src/com/intellij/Patches.java +++ b/platform/util/src/com/intellij/Patches.java @@ -137,4 +137,7 @@ public class Patches { public static final boolean JDK_BUG_ID_8032832 = SystemInfo.isJavaVersionAtLeast("1.8.0_20"); public static final boolean JDK_BUG_ID_8147994 = !SystemInfo.isMac && !SystemInfo.isJavaVersionAtLeast("1.8.0_102"); + + // IDEA-175066 / https://stackoverflow.com/questions/31676277/stackoverflowerror-in-regular-expression + public static final boolean JDK_SOE_IN_REGEXP = (true); } \ No newline at end of file