4.7 KiB
Entity Framework Core — CWE-407 Disclosure Brief
2026-03-27 · Patch available — awaiting upstream merge
Finding
Three O(n²) defects in Entity Framework Core's metadata and FK discovery systems. All patched. Patches ready for upstream review. Two defects fire on every SaveChanges() call; one fires at model build time.
The Defects
efcore-0001 (PATCHED — HIGH): Metadata/Internal/PropertyExtensions.cs:72
// In FindGenerationProperty() — BFS FK traversal:
// Called from KeyPropagator.PropagateValue() on every SaveChanges():
var visited = new List<IProperty>();
while (queue.Count > 0) {
var property = queue.Dequeue();
if (visited.Contains(property)) // O(D) — List<IProperty>.Contains()
continue;
visited.Add(property);
// ...
}
visited is a List<IProperty>. Contains() performs a linear scan over the growing visited list. O(D²) where D = FK chain depth.
efcore-0002 (PATCHED — HIGH): Metadata/IReadOnlyProperty.cs:248
// In AddPrincipals() — recursive FK traversal:
private static void AddPrincipals(
List<IReadOnlyProperty> visited,
IReadOnlyProperty property)
{
if (visited.Contains(property)) // O(P) — List<T>.Contains()
return;
visited.Add(property);
// recursive call
}
visited passed down the recursion is a List<T>. O(P²) over P principal chain length.
efcore-0003 (PATCHED — MEDIUM): Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs:505,746
// In foreign key property discovery — model build time:
if (foreignKeyProperties.Contains(property)) // O(K) — IReadOnlyList.Contains()
foreignKeyProperties is IReadOnlyList<IProperty>. Contains() is O(K) inside key-property nested loops during model build.
Complexity Proof
efcore-0001: For D nodes in FK chain:
- BFS visits D nodes; each
Contains()scans growing visited list - Total: 0 + 1 + ... + (D-1) = O(D²)
At D=500: defective=124,750 comparisons, fixed=500. 250× op reduction.
efcore-0002: For P principals in chain:
- Each recursive call: O(P)
Contains()scan - Total: O(P²)
At P=500: 250× op reduction.
efcore-0003: For K FK properties and Kp key properties and Fp foreign key properties:
- Nested loops: O(K × Kp × Fp)
6× op reduction at typical model sizes.
Impact
Entity Framework Core is the ORM for all .NET applications — ASP.NET Core, Blazor, Azure Functions, and every .NET enterprise application using SQL Server, PostgreSQL, MySQL, or SQLite.
efcore-0001 and efcore-0002 fire on every SaveChanges() call — the fundamental EF Core operation for writing data. In a web application processing 100 requests/second each calling SaveChanges(), these defects execute millions of times per day. Applications with deep FK relationships (common in domain-driven design with entity graphs) maximize D and hit worst case on every write.
efcore-0003 fires once per model build (application startup, or dynamic model creation), but for models with many FK relationships the overhead is measurable.
The Fix
efcore-0001: Replace List<IProperty> with HashSet<IProperty>:
// Before
var visited = new List<IProperty>();
if (visited.Contains(property)) ...
// After
// CWE-407 fix: HashSet<IProperty> for O(1) Contains() instead of O(D) List scan.
var visited = new HashSet<IProperty>();
if (!visited.Add(property)) continue; // Add() returns false if already present
efcore-0002: Pass HashSet<T> down the recursion:
// Before
private static void AddPrincipals(List<IReadOnlyProperty> visited, ...)
// After
// CWE-407 fix: HashSet<T> for O(1) Contains() throughout principal chain traversal.
private static void AddPrincipals(HashSet<IReadOnlyProperty> visited, ...)
IProperty and IReadOnlyProperty implement reference equality — no custom GetHashCode needed.
Patch
Fix available: defects/efcore/patch/efcore-0001-0003-visited-hashset.patch
Three-location patch across PropertyExtensions.cs, IReadOnlyProperty.cs, and ForeignKeyPropertyDiscoveryConvention.cs.
Unit test: EfCoreTest 3/3 pass. efcore-0001: 250× speedup. efcore-0002: 250× speedup. efcore-0003: 6× speedup.
What We Ask
A patch is ready for review.
- Confirm receipt and assign a GitHub Security Advisory or issue reference (dotnet/efcore).
- Assess severity — efcore-0001 and efcore-0002 fire on every
SaveChanges()call in applications with FK relationships. - Coordinate a disclosure date — we are targeting 90 days from first contact.
- We will credit the EF Core team in the public disclosure. Preferred acknowledgment format welcome.
Contact: see cover email. This brief is confidential until coordinated disclosure.