java-topology/whitepaper/outreach/hibernate.md

4.9 KiB
Raw Permalink Blame History

Hibernate ORM — CWE-407 Disclosure Brief

2026-03-27 · Patch available — awaiting upstream merge

Finding

Five O(n²) defects in Hibernate ORM's schema mapping layer, all sharing the same root cause: ArrayList used as a dedup-tracking container with contains() called before add() in loops over schema columns, index columns, and FK second-pass queues. All patched. Patches ready for upstream review.

The Defects

hibernate-0001 (PATCHED — HIGH): mapping/Constraint.java

// In addColumn() — called per column during schema mapping:
if (!columns.contains(column)) {
    columns.add(column);
}
// columns is ArrayList<Column> — contains() is O(C)

O(C²) cost during SessionFactory build time. Every unique constraint, primary key, and check constraint accumulates columns through this path.

hibernate-0002 (PATCHED — HIGH): mapping/ForeignKey.java

// In addReferencedColumn() — per FK column:
if (!referencedColumns.contains(column)) {
    referencedColumns.add(column);
}
// referencedColumns is ArrayList<Column> — contains() is O(C)

Same root cause as hibernate-0001 on the FK referenced-column dedup path.

hibernate-0003 (PATCHED — HIGH): mapping/Index.java

// In addColumn() — per index column:
if (!columns.contains(column)) {
    columns.add(column);
}
// columns is ArrayList<Column> — contains() is O(C)

Same root cause as hibernate-0001 on the index column dedup path.

hibernate-0004 (PATCHED — HIGH): boot/model/process/spi/InFlightMetadataCollectorImpl.java

// In buildRecursiveOrderedFkSecondPasses():
if (!fkSecondPasses.contains(secondPass)) {
    fkSecondPasses.add(0, secondPass);  // O(D) prepend
}
// fkSecondPasses is ArrayList — contains() O(D), add(0,…) O(D)

O(D²) over D FK second-passes in inheritance chain processing.

hibernate-0005 (PATCHED — HIGH): engine/internal/StatisticalLoggingSessionEventListener.java

// In orderHierarchy() — recursive sort:
if (!ordered.contains(type)) {
    ordered.add(type);
}
// ordered is ArrayList — contains() O(T) per type in T-type hierarchy

O(T²) over T entity types in the hierarchy ordering sort.

Complexity Proof

All five defects share the same structure: ArrayList.contains() called before ArrayList.add() in a loop. For a list of size N built incrementally:

  • Element 1: 0 comparisons
  • Element 2: 1 comparison
  • ...
  • Element N: N-1 comparisons
  • Total: 0 + 1 + ... + (N-1) = O(N²)

At N=5,000 columns: defective=12,497,500 comparisons, fixed=5,000. 19× speedup confirmed by unit test HibernateConstraintColumnTest.

The fix — LinkedHashSet<Column> — preserves insertion order (required for correct column ordering in generated DDL) while providing O(1) contains().

Impact

Hibernate ORM is the dominant Java ORM — used in Spring Boot applications, Jakarta EE, Quarkus, and virtually every Java enterprise application that uses a relational database. SessionFactory build time (where these defects activate) runs:

  • On every application startup
  • On every hot-reload in development (Spring DevTools, Quarkus dev mode)
  • On every test suite run that creates an EntityManagerFactory
  • In AWS Lambda cold starts and Kubernetes pod restarts

Applications with large schemas (many tables, many foreign keys, many constraints) maximize C and hit worst case on every startup. Enterprise applications with 200+ entity classes and complex inheritance hierarchies are common.

The Fix

Replace ArrayList with LinkedHashSet throughout (preserves insertion order):

// Before — hibernate-0001/0002/0003
private final List<Column> columns = new ArrayList<>();
if (!columns.contains(column)) {
    columns.add(column);
}

// After
// CWE-407 fix: LinkedHashSet for O(1) contains() with insertion-order preservation.
private final Set<Column> columns = new LinkedHashSet<>();
columns.add(column);  // Set.add() is idempotent — no contains() needed

Column implements equals()/hashCode() — no additional changes needed.

Patch

Fix available: defects/hibernate/patch/hibernate-0001-0005-column-linkedhashset.patch

Five-location patch across Constraint.java, ForeignKey.java, Index.java, InFlightMetadataCollectorImpl.java, and StatisticalLoggingSessionEventListener.java.

Unit test: HibernateConstraintColumnTest19× speedup at N=5,000 columns.

What We Ask

A patch is ready for review.

  1. Confirm receipt and assign a Hibernate JIRA reference (hibernate.atlassian.net).
  2. Assess severity — all five defects fire on every SessionFactory build, which runs at every application startup.
  3. Coordinate a disclosure date — we are targeting 90 days from first contact.
  4. We will credit the Hibernate team in the public disclosure. Preferred acknowledgment format welcome.

Contact: see cover email. This brief is confidential until coordinated disclosure.