dubbo-0001: AnnotationUtils.getAllMetaAnnotations recurses into meta-annotation graph without a visited set — diamond shapes cause O(2^D) calls (UNDF-2026-000000238) cxf-0001: SchemaUtil.parseImports + WSDLServiceBuilder.parseImports use ArrayList.contains as visited guard on WSDL import graph — O(N²) membership checks (UNDF-2026-000000237) Struts, RabbitMQ, Camel: CLEAN
4.4 KiB
UNDF: UNDF-2026-000000238
dubbo-0001: AnnotationUtils.getAllMetaAnnotations diamond recursion O(2^D)
Classification
- CWE: CWE-407 (Inefficient Algorithmic Complexity)
- Severity: MEDIUM
- Component: Apache Dubbo —
dubbo-common - File:
dubbo-common/src/main/java/org/apache/dubbo/common/utils/AnnotationUtils.java - Method:
getAllMetaAnnotations(Class<? extends Annotation>, Predicate<Annotation>...)
Description
getAllMetaAnnotations recursively traverses the meta-annotation graph of a Java annotation
type without a visited set. When the annotation DAG has a diamond shape — two annotations B and
C both carry meta-annotation D, and a top-level annotation A carries both B and C — the method
visits D twice. At depth D the call count is O(2^D).
Defect Code
// dubbo-common/.../utils/AnnotationUtils.java lines 281-296
static List<Annotation> getAllMetaAnnotations(
Class<? extends Annotation> annotationType, Predicate<Annotation>... annotationsToFilter) {
List<Annotation> allMetaAnnotations = new LinkedList<>();
List<Annotation> metaAnnotations = getMetaAnnotations(annotationType); // direct meta-annotations
allMetaAnnotations.addAll(metaAnnotations);
for (Annotation metaAnnotation : metaAnnotations) {
// Get the nested meta annotations recursively
allMetaAnnotations.addAll(getAllMetaAnnotations(metaAnnotation.annotationType()));
// NO visited set: diamond causes 2^D recursive calls
}
return unmodifiableList(filterAll(allMetaAnnotations, annotationsToFilter));
}
Diamond Example
@A
|-meta-> @B
| |-meta-> @D <-- visited ONCE per path
|-meta-> @C
|-meta-> @D <-- visited AGAIN (second path)
Depth 1 diamond: getAllMetaAnnotations(@D) called 2 times
Depth 2 diamond: 4 times
Depth D: 2^D times
In a Dubbo deployment with composite Dubbo service annotations (e.g., @DubboService carrying
@Service, @Component, and custom composed annotations), this pattern can occur at annotation
processor startup time. The method is called from findMetaAnnotations, which is called during
service registration and reference injection on every annotated class.
Callers
findMetaAnnotations(Class<? extends Annotation>, Class<A>)— line 334findMetaAnnotations(AnnotatedElement, Class<A>)— line 351 (callsfindMetaAnnotationson each declared annotation's type, which internally callsgetAllMetaAnnotations)
Complexity
| D (diamond depth) | Call count | Speedup with fix |
|---|---|---|
| 1 | 2 | 2x |
| 5 | 32 | 32x |
| 10 | 1024 | 1024x |
| 15 | 32768 | ~33000x |
In practice D is small (3-5 for typical Java meta-annotation hierarchies), but Dubbo's rich
composed-annotation ecosystem (e.g., @DubboService + @EnableDubbo + framework annotations)
can create D=5+ chains, giving 32x+ redundant work at startup.
Fix
Pass a Set<Class<? extends Annotation>> visited accumulator through the recursion:
static List<Annotation> getAllMetaAnnotations(
Class<? extends Annotation> annotationType, Predicate<Annotation>... annotationsToFilter) {
Set<Class<? extends Annotation>> visited = new LinkedHashSet<>();
return getAllMetaAnnotations(annotationType, visited, annotationsToFilter);
}
private static List<Annotation> getAllMetaAnnotations(
Class<? extends Annotation> annotationType,
Set<Class<? extends Annotation>> visited,
Predicate<Annotation>... annotationsToFilter) {
List<Annotation> allMetaAnnotations = new LinkedList<>();
List<Annotation> metaAnnotations = getMetaAnnotations(annotationType);
allMetaAnnotations.addAll(metaAnnotations);
for (Annotation metaAnnotation : metaAnnotations) {
Class<? extends Annotation> metaType = metaAnnotation.annotationType();
if (visited.add(metaType)) { // skip if already visited
allMetaAnnotations.addAll(getAllMetaAnnotations(metaType, visited));
}
}
return unmodifiableList(filterAll(allMetaAnnotations, annotationsToFilter));
}
Evidence
- File:
dubbo-common/src/main/java/org/apache/dubbo/common/utils/AnnotationUtils.java - Lines 281-296: recursive call with no visited guard
- No
Set,Map, orcacheparameter in method signature getMetaAnnotationsexcludes@Target,@Retention,@Documentedto avoid Java built-in annotation self-references — but does not prevent application-defined diamond shapes