ICU-23449 Fix Null-dereference in RBBITableBuilder::calcLastPos
See #4058
diff --git a/icu4c/source/common/rbbitblb.cpp b/icu4c/source/common/rbbitblb.cpp
index 4d5eeef..f714b8f 100644
--- a/icu4c/source/common/rbbitblb.cpp
+++ b/icu4c/source/common/rbbitblb.cpp
@@ -125,6 +125,9 @@ void RBBITableBuilder::buildForwardTable() {
// expression.
//
fTree->flattenSets(*fStatus, 0);
+ if (U_FAILURE(*fStatus)) {
+ return;
+ }
#ifdef RBBI_DEBUG
if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "stree")) {
RBBIDebugPuts("\nParse tree after flattening Unicode Set references.");
diff --git a/icu4c/source/test/intltest/rbbitst.cpp b/icu4c/source/test/intltest/rbbitst.cpp
index 4d7a885..5a6be22 100644
--- a/icu4c/source/test/intltest/rbbitst.cpp
+++ b/icu4c/source/test/intltest/rbbitst.cpp
@@ -159,6 +159,7 @@ void RBBITest::runIndexedTest( int32_t index, UBool exec, const char* &name, cha
TESTCASE_AUTO(TestBug22602);
TESTCASE_AUTO(TestBug22636);
TESTCASE_AUTO(TestLookaheadPolychromy);
+ TESTCASE_AUTO(TestBug23449);
#if U_ENABLE_TRACING
TESTCASE_AUTO(TestTraceCreateCharacter);
@@ -4999,4 +5000,23 @@ void RBBITest::TestLookaheadPolychromy() {
}
}
+void RBBITest::TestBug23449() {
+ // Construct a rules string with long concatenation to trigger depth limit.
+ UnicodeString rules;
+ int length = 3600;
+ for (int i = 0; i < length; ++i) {
+ rules.append(u"[a]");
+ }
+ rules.append(u';'); // Rule terminator
+
+ UErrorCode status = U_ZERO_ERROR;
+ UParseError parseError;
+ // We expect this to fail with U_INPUT_TOO_LONG_ERROR during rule compilation, not crash.
+ LocalPointer<RuleBasedBreakIterator> bi(new RuleBasedBreakIterator(rules, parseError, status));
+
+ if (status != U_INPUT_TOO_LONG_ERROR) {
+ errln("Expected U_INPUT_TOO_LONG_ERROR, but got: %s", u_errorName(status));
+ }
+}
+
#endif // #if !UCONFIG_NO_BREAK_ITERATION
diff --git a/icu4c/source/test/intltest/rbbitst.h b/icu4c/source/test/intltest/rbbitst.h
index 142c09a..42f9259 100644
--- a/icu4c/source/test/intltest/rbbitst.h
+++ b/icu4c/source/test/intltest/rbbitst.h
@@ -108,6 +108,7 @@ class RBBITest: public IntlTest {
void TestBug22602();
void TestBug22636();
void TestLookaheadPolychromy();
+ void TestBug23449();
#if U_ENABLE_TRACING
void TestTraceCreateCharacter();
diff --git a/icu4j/main/core/src/main/java/com/ibm/icu/text/RBBINode.java b/icu4j/main/core/src/main/java/com/ibm/icu/text/RBBINode.java
index a6014d7..b47ce99 100644
--- a/icu4j/main/core/src/main/java/com/ibm/icu/text/RBBINode.java
+++ b/icu4j/main/core/src/main/java/com/ibm/icu/text/RBBINode.java
@@ -149,22 +149,29 @@ class RBBINode {
//
// -------------------------------------------------------------------------
RBBINode cloneTree() {
+ return cloneTree(0);
+ }
+
+ RBBINode cloneTree(int depth) {
+ if (depth > kRecursiveDepthLimit) {
+ throw new IllegalArgumentException("The input is too long");
+ }
RBBINode n;
if (fType == RBBINode.varRef) {
// If the current node is a variable reference, skip over it
// and clone the definition of the variable instead.
- n = fLeftChild.cloneTree();
+ n = fLeftChild.cloneTree(depth + 1);
} else if (fType == RBBINode.uset) {
n = this;
} else {
n = new RBBINode(this);
if (fLeftChild != null) {
- n.fLeftChild = fLeftChild.cloneTree();
+ n.fLeftChild = fLeftChild.cloneTree(depth + 1);
n.fLeftChild.fParent = n;
}
if (fRightChild != null) {
- n.fRightChild = fRightChild.cloneTree();
+ n.fRightChild = fRightChild.cloneTree(depth + 1);
n.fRightChild.fParent = n;
}
}
@@ -196,7 +203,7 @@ RBBINode flattenVariables(int depth) {
throw new IllegalArgumentException("The input is too long");
}
if (fType == varRef) {
- RBBINode retNode = fLeftChild.cloneTree();
+ RBBINode retNode = fLeftChild.cloneTree(depth + 1);
retNode.fRuleRoot = this.fRuleRoot;
retNode.fChainIn = this.fChainIn;
return retNode;
@@ -222,6 +229,13 @@ RBBINode flattenVariables(int depth) {
//
// -------------------------------------------------------------------------
void flattenSets() {
+ flattenSets(0);
+ }
+
+ void flattenSets(int depth) {
+ if (depth > kRecursiveDepthLimit) {
+ throw new IllegalArgumentException("The input is too long");
+ }
Assert.assrt(fType != setRef);
if (fLeftChild != null) {
@@ -229,10 +243,10 @@ void flattenSets() {
RBBINode setRefNode = fLeftChild;
RBBINode usetNode = setRefNode.fLeftChild;
RBBINode replTree = usetNode.fLeftChild;
- fLeftChild = replTree.cloneTree();
+ fLeftChild = replTree.cloneTree(depth + 1);
fLeftChild.fParent = this;
} else {
- fLeftChild.flattenSets();
+ fLeftChild.flattenSets(depth + 1);
}
}
@@ -241,11 +255,11 @@ void flattenSets() {
RBBINode setRefNode = fRightChild;
RBBINode usetNode = setRefNode.fLeftChild;
RBBINode replTree = usetNode.fLeftChild;
- fRightChild = replTree.cloneTree();
+ fRightChild = replTree.cloneTree(depth + 1);
fRightChild.fParent = this;
// delete setRefNode;
} else {
- fRightChild.flattenSets();
+ fRightChild.flattenSets(depth + 1);
}
}
}
diff --git a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITest.java b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITest.java
index a1be7fb..0ca1c8a 100644
--- a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITest.java
+++ b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITest.java
@@ -1342,4 +1342,22 @@ public void TestLookaheadPolychromy() {
+ actual);
}
}
+
+ @Test
+ public void TestBug23449() {
+ StringBuilder rules = new StringBuilder();
+ int length = 3600;
+ for (int i = 0; i < length; ++i) {
+ rules.append("[a]");
+ }
+ rules.append(";");
+
+ try {
+ new RuleBasedBreakIterator(rules.toString());
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ assertEquals("Exception message", "The input is too long", e.getMessage());
+ }
+ }
}