From 019b2c69bd5b8cf8fa9a21bd5c4479204b27cb36 Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Wed, 08 Jul 2026 16:05:46 +0000
Subject: [PATCH] [#665] Fix StackOverflowError while parsing long ACI with repetitive targets (#666)
---
opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/Aci.java | 63 ++++++++++++++++++++++---------
1 files changed, 45 insertions(+), 18 deletions(-)
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/Aci.java b/opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/Aci.java
index 8e69671..d938a09 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/Aci.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/Aci.java
@@ -13,6 +13,7 @@
*
* Copyright 2008 Sun Microsystems, Inc.
* Portions Copyright 2010-2016 ForgeRock AS.
+ * Portions Copyright 2026 3A Systems, LLC
*/
package org.opends.server.authorization.dseecompat;
@@ -45,12 +46,26 @@
/** Regular expression matching a word group. */
public static final String WORD_GROUP="(\\w+)";
+ /**
+ * Possessive variant of {@link #WORD_GROUP}. Use inside unbounded
+ * repetition so the regex engine matches iteratively instead of
+ * recursing once per repetition (issue #665).
+ */
+ public static final String WORD_GROUP_POSSESSIVE="(\\w++)";
+
/** Regular expression matching a word group at the start of a pattern. */
static final String WORD_GROUP_START_PATTERN = "^" + WORD_GROUP;
/** Regular expression matching a white space. */
public static final String ZERO_OR_MORE_WHITESPACE="\\s*";
+ /**
+ * Possessive variant of {@link #ZERO_OR_MORE_WHITESPACE}. Use inside
+ * unbounded repetition so the regex engine matches iteratively instead of
+ * recursing once per repetition (issue #665).
+ */
+ public static final String ZERO_OR_MORE_WHITESPACE_POSSESSIVE="\\s*+";
+
/** Regular expression matching a white space at the start of a pattern. */
public static final String ZERO_OR_MORE_WHITESPACE_START_PATTERN =
"^" + ZERO_OR_MORE_WHITESPACE ;
@@ -106,13 +121,17 @@
"\\+" + ZERO_OR_MORE_WHITESPACE;
/** Regular expression used to do quick check of OID string. */
- private static final String OID_NAME = "[\\d.\\*]*";
+ private static final String OID_NAME = "[\\d.\\*]*+";
- /** Regular expression that matches one or more OID_NAME's separated by the "||" token. */
- private static final String oidListRegex = ZERO_OR_MORE_WHITESPACE +
- OID_NAME + ZERO_OR_MORE_WHITESPACE + "(" +
- LOGICAL_OR + ZERO_OR_MORE_WHITESPACE + OID_NAME +
- ZERO_OR_MORE_WHITESPACE + ")*";
+ /**
+ * Regular expression that matches one or more OID_NAME's separated by the
+ * "||" token. Possessive quantifiers keep the unbounded repetition from
+ * recursing in the regex engine (issue #665).
+ */
+ private static final String oidListRegex = ZERO_OR_MORE_WHITESPACE_POSSESSIVE +
+ OID_NAME + ZERO_OR_MORE_WHITESPACE_POSSESSIVE + "(" +
+ LOGICAL_OR + ZERO_OR_MORE_WHITESPACE_POSSESSIVE + OID_NAME +
+ ZERO_OR_MORE_WHITESPACE_POSSESSIVE + ")*+";
/** ACI_ADD is used to set the container rights for a LDAP add operation. */
public static final int ACI_ADD = 0x0020;
@@ -247,19 +266,27 @@
public static Aci decode (ByteSequence byteString, DN dn)
throws AciException {
String input=byteString.toString();
- //Perform a quick pattern check against the string to catch any
- //obvious syntax errors.
- if (!Pattern.matches(aciRegex, input)) {
- throw new AciException(WARN_ACI_SYNTAX_GENERAL_PARSE_FAILED.get(input));
+ try {
+ //Perform a quick pattern check against the string to catch any
+ //obvious syntax errors.
+ if (!Pattern.matches(aciRegex, input)) {
+ throw new AciException(WARN_ACI_SYNTAX_GENERAL_PARSE_FAILED.get(input));
+ }
+ //Decode the body first.
+ AciBody body=AciBody.decode(input);
+ //Create a substring from the start of the string to start of
+ //the body. That should be the target.
+ String targetStr = input.substring(0, body.getMatcherStartPos());
+ //Decode that target string using the substring.
+ AciTargets targets=AciTargets.decode(targetStr, dn);
+ return new Aci(input, dn, body, targets);
+ } catch (StackOverflowError e) {
+ //Defense in depth: a pathological ACI can overflow the regex
+ //engine's recursion. StackOverflowError is an Error and would
+ //escape catch(Exception) handlers, killing the worker thread, so
+ //treat the value as an invalid ACI instead (issue #665).
+ throw new AciException(WARN_ACI_SYNTAX_GENERAL_PARSE_FAILED.get(input), e);
}
- //Decode the body first.
- AciBody body=AciBody.decode(input);
- //Create a substring from the start of the string to start of
- //the body. That should be the target.
- String targetStr = input.substring(0, body.getMatcherStartPos());
- //Decode that target string using the substring.
- AciTargets targets=AciTargets.decode(targetStr, dn);
- return new Aci(input, dn, body, targets);
}
/**
--
Gitblit v1.10.0