From 35037dac7b6e080c7d1dae31a64d0d5012b1b40c Mon Sep 17 00:00:00 2001 From: Legohuman Date: Sat, 10 Aug 2019 00:33:24 +0200 Subject: [PATCH] HHH-13259 Fix StackOverflowError in StringHelper Before fix method org.hibernate.internal.util.StringHelper#replace matched placeholders illegally in case when ordinal parameters list was expanded. Ex. placeholder ?1 was matched with ?11, ?12, ?13 etc. For queries with 2 or more IN clauses with different collections there were a situation when ?1 from the first clause matched with already expanded placeholders from the second collection. Each match led to recursive call of replace method. If collection in second clause was very long then StackOverflowError occurred. Fix adds check of partial placeholder match for wholeWords mode which is used in expanding list parameters. Partial matches are skipped during replace. --- .../hibernate/internal/util/StringHelper.java | 20 ++++++++++++++++++- .../hibernate/test/util/StringHelperTest.java | 20 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java index 9c658ec37f..f79b03a651 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java @@ -123,7 +123,7 @@ public final class StringHelper { if ( template == null ) { return null; } - int loc = template.indexOf( placeholder ); + int loc = indexOfPlaceHolder( template, placeholder, wholeWords ); if ( loc < 0 ) { return template; } @@ -189,6 +189,24 @@ public final class StringHelper { return buf.toString(); } + private static int indexOfPlaceHolder(String template, String placeholder, boolean wholeWords) { + if ( wholeWords ) { + int placeholderIndex = -1; + boolean isPartialPlaceholderMatch; + do { + placeholderIndex = template.indexOf( placeholder, placeholderIndex + 1 ); + isPartialPlaceholderMatch = placeholderIndex != -1 && + template.length() > placeholderIndex + placeholder.length() && + Character.isJavaIdentifierPart( template.charAt( placeholderIndex + placeholder.length() ) ); + } while ( placeholderIndex != -1 && isPartialPlaceholderMatch ); + + return placeholderIndex; + } + else { + return template.indexOf( placeholder ); + } + } + /** * Used to find the ordinal parameters (e.g. '?1') in a string. */ diff --git a/hibernate-core/src/test/java/org/hibernate/test/util/StringHelperTest.java b/hibernate-core/src/test/java/org/hibernate/test/util/StringHelperTest.java index 299b60bc32..545a638753 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/util/StringHelperTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/util/StringHelperTest.java @@ -100,4 +100,24 @@ public class StringHelperTest extends BaseUnitTestCase { Assert.assertFalse( StringHelper.isQuoted( "a", sqlServerDialect ) ); } + @Test + public void replaceRepeatingPlaceholdersWithoutStackOverflow() { + String ordinalParameters = generateOrdinalParameters( 3, 19999 ); + String result = StringHelper.replace( + "select * from books where category in (?1) and id in(" + ordinalParameters + ") and parent_category in (?1) and id in(" + ordinalParameters + ")", + "?1", "?1, ?2", true, true ); + assertEquals( "select * from books where category in (?1, ?2) and id in(" + ordinalParameters + ") and parent_category in (?1, ?2) and id in(" + ordinalParameters + ")", result ); + } + + private String generateOrdinalParameters(int startPosition, int endPosition) { + StringBuilder builder = new StringBuilder(); + for ( int i = startPosition; i <= endPosition; i++ ) { + builder.append( '?' ).append( i ); + if ( i < endPosition ) { + builder.append( ", " ); + } + } + return builder.toString(); + } + }