[#648] slow DN.valueOf / AVA normalization for nested DN-syntax values#649
Merged
Merged
Conversation
… normalization Parsing or normalizing a DN that uses DN-syntax attribute values (e.g. aliasedObjectName / OID 2.5.4.1) whose values are themselves crafted to contain many nested DN-syntax values could take minutes and exhaust memory. Two independent causes are addressed: 1. RDN.validateAvas() sorted the AVAs of a multi-valued RDN using the full AVA ordering, which normalizes the attribute values, only to detect duplicate attribute types. Detecting duplicate types does not require value normalization, so it now sorts by attribute type only. This makes parsing of such DNs fail fast with a "duplicate AVA types" error instead of running the expensive normalization. 2. Normalizing a DN-syntax value recursively parses and normalizes the value as a DN. Each nesting level escapes the reserved separator bytes of the level below, which roughly doubles the number of reserved bytes per level, so the normalized form grows exponentially with the nesting depth. DistinguishedNameEqualityMatchingRuleImpl now bounds the recursion depth and the size of the normalized value, throwing a DecodeException when the limits are exceeded. The AVA then falls back to byte-wise comparison. Adds tests covering both fixes.
maximthomas
approved these changes
Jun 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Reported in OpenDJ issue #648.
The following used to take more than 2.5 minutes:
and, equivalently, normalizing a single deeply-nested DN-syntax value
(
AVA.getOrderingNormalizedValue()).Root cause
The attribute type
2.5.4.1isaliasedObjectName, whose syntax is DN andwhose equality matching rule is
distinguishedNameMatch. Normalizing such avalue re-parses it as a DN and normalizes it recursively
(
DistinguishedNameEqualityMatchingRuleImpl.normalizeAttributeValue→DN.valueOf(...).toNormalizedByteString()).Two problems compound each other:
Unnecessary normalization during RDN validation. When building a
multi-valued RDN,
RDN.validateAvas()calledArrays.sort(sortedAVAs),which uses
AVA.compareTo→getOrderingNormalizedValue()→ the recursiveDN normalization. The sort is only used to detect duplicate attribute
types, which does not need the values at all.
Exponential growth of the normalized form.
RDN.toNormalizedByteStringprefixes each RDN with a separator byte (
0x00) andAVA.escapeBytesescapes the reserved bytes (
0x00,0x01,0x02) by prefixing them with0x02. Each nesting level therefore roughly doubles the number of reservedbytes, so a value with N nested DN levels produces a normalized form of
size ~
2^N. At ~30 levels this is ~10^9 bytes → minutes of CPU andOutOfMemoryError.Fix
1.
RDN.validateAvas— detect duplicate types without normalizing valuesopendj-core/.../ldap/RDN.javadefault(3+ AVAs) branch now sorts a copy of the AVAs using a newATTRIBUTE_TYPE_COMPARATORthat compares only the attribute types,instead of the full
AVA.compareTo(which normalizes values).DN.valueOf(dnString)from the report fail fast withERR_RDN_DUPLICATE_AVA_TYPES(the top RDN contains2.5.4.1twice) insteadof triggering the expensive normalization.
2.
DistinguishedNameEqualityMatchingRuleImpl— bound recursive normalizationopendj-core/.../ldap/schema/DistinguishedNameEqualityMatchingRuleImpl.javaMAX_NESTED_DN_DEPTH = 100) forstack safety.
(
MAX_NORMALIZED_VALUE_SIZE = 1 MiB). The first nesting level whosenormalized form exceeds the limit throws a
DecodeException; the enclosingAVA catches it (existing behaviour in
AVA.getEqualityNormalizedValue/getOrderingNormalizedValue) and falls back to a byte-wise comparison.This caps the total work and memory regardless of the crafted nesting depth.
ERR_ATTR_SYNTAX_DN_MAX_DEPTHadded tocore.properties.Legitimate (shallow) nested DN-syntax values continue to normalize exactly as
before; only pathologically deep/large values degrade to byte-wise comparison.
Files changed
opendj-core/src/main/java/org/forgerock/opendj/ldap/RDN.javaopendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DistinguishedNameEqualityMatchingRuleImpl.javaopendj-core/src/main/resources/com/forgerock/opendj/ldap/core.propertiesopendj-core/src/test/java/org/forgerock/opendj/ldap/RDNTestCase.javaopendj-core/src/test/java/org/forgerock/opendj/ldap/DNTestCase.javaopendj-core/src/test/java/org/forgerock/opendj/ldap/schema/DistinguishedNameEqualityMatchingRuleTest.javaTests
New tests (all run under a
timeOutso a regression fails instead of hanging):RDNTestCase.testDuplicateDnSyntaxAvasAreDetectedQuickly— a multi-valued RDNwith duplicate DN-syntax types is rejected quickly.
RDNTestCase.testDistinctDnSyntaxAvasAreValidatedQuickly— a multi-valued RDNwith distinct DN-syntax types is built quickly.
DNTestCase.testValueOfWithDuplicateNestedDnSyntaxAvasIsFast— the exactreproduction from the report fails fast.
DistinguishedNameEqualityMatchingRuleTest.testNormalizationOfDeeplyNestedDnValueIsBounded— normalizing a deeply nested DN-syntax value is bounded.
Verification
Before the fix, the reproduction normalization ran for minutes and ended with
OutOfMemoryError(even with-Xmx256m). After the fix:DN.valueOf(dnString)throwsLocalizedIllegalArgumentExceptionimmediately.Affected test classes pass: