# 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

```java
// 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 334
- `findMetaAnnotations(AnnotatedElement, Class<A>)` — line 351 (calls `findMetaAnnotations` on
  each declared annotation's type, which internally calls `getAllMetaAnnotations`)

## 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:

```java
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`, or `cache` parameter in method signature
- `getMetaAnnotations` excludes `@Target`, `@Retention`, `@Documented` to avoid Java built-in
  annotation self-references — but does not prevent application-defined diamond shapes
