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());
+        }
+    }
 }