Vulnerability GHSA-wp2g-vpjj-g53r

High Risk
HIGH RISK
CVSS Score: 7.5
Score Range: 7.0–8.9
High severity vulnerabilities (CVSS 7.0–8.9). Serious vulnerabilities that should be prioritized soon after critical fixes.
6 hours ago
October 08, 2026 at 04:31 PM UTC
Excelize ANCHORARRAY: mutually-referencing array formulas recurse unboundedly via re-entrant CalcCellValue, causing a fatal stack overflow
v2.8.1 - v2.11.0
v2.8.1 - v2.11.0

Summary

Excelize ANCHORARRAY: mutually-referencing array formulas recurse unboundedly via re-entrant CalcCellValue, causing a fatal stack overflow

Details

Summary

ANCHORARRAY (calc.go:15137 on current master) evaluates each cell of the referenced spill range by calling the exported CalcCellValue, which unconditionally constructs a fresh calcContext — fresh entry marker, fresh iterations map, full MaxCalcIterations budget (calc.go:896-900). The circular-reference control only exists within one context: the entry-exclusion marker and per-ref iteration budget are fields of that single context, and completion-based caching (formulaArgCache/calcRawCache) only ever stores results of evaluations that finish.

During a pure cycle no nested evaluation ever finishes, so nothing is ever cached to break the recursion, and each hop re-arms the entire budget. Two ordinary, Excel-legal constructs in an attacker-supplied workbook are enough:

  • A1 (dynamic array formula, ref A1:A1): _xlfn.ANCHORARRAY($B$1)
  • B1 (dynamic array formula, ref B1:B1): _xlfn.ANCHORARRAY($A$1)

→ CalcCellValue → calcCellValue → evalInfixExp → parseReference → cellResolver → … → ANCHORARRAY → CalcCellValue → … forever, ending in a fatal, unrecoverable Go runtime error: runtime: goroutine stack exceeds …-byte limit / fatal error: stack overflow. Go stack overflows cannot be recovered — the whole process aborts.

Details

  • The terminating edge the iterations gate is supposed to provide does not exist across contexts: there is no in-flight tracking shared between nested CalcCellValue calls.
  • Both formulas are ordinary Excel-legal constructs; no exotic XML is required.
  • Excelize's own APIs reach formula evaluation on untrusted cells implicitly (e.g. pivotTable.go:538, picture.go:985/1141, col.go:892), so a service that merely adds a pivot table or a picture over such a workbook dies.
  • No option value prevents it: MaxCalcIterations is irrelevant because every hop gets a fresh budget.
  • Measured: a 64 MB stack budget is exhausted in ~0.09 s; the ~1 GB default in ~1–2 s.

PoC

A standalone program (public API only) was provided to the maintainer by email (4-anchorarray-recursion): NewFile + SetCellFormula with FormulaOpts{Type: array, Ref: A1:A1 / B1:B1}, then CalcCellValue("Sheet1","A1"). On master ecd99d761fe0 (2026-09-08) the process aborts with fatal error: stack overflow; with the proposed patch the cycle terminates normally (CYCLE_TERMINATED) and existing calc tests pass.

Impact

An attacker ships a workbook containing the two formulas; any service that evaluates a formula over those cells — directly via CalcCellValue, or implicitly via AddPivotTable / AddPicture / auto-fit — aborts. Remote, unauthenticated, process-fatal, no configuration prevents it.

Proposed fix

Evaluate spill-range cells through the current calculation context — fn.f.cellResolver(fn.ctx, …) instead of the exported CalcCellValue — so the entry check and iterations gate of the running calculation apply. cellResolver returns the typed value directly (dropping a string round-trip); an ArgEmpty → "" shim preserves the existing ToNumber behavior for empty spill cells, and a fresh-context fallback covers the legacy nil-ctx test paths. A complete patch has been provided to the maintainer.

Timeline

Published
6 hours ago
October 08, 2026 at 04:31 PM UTC
Last Modified
6 hours ago
October 08, 2026 at 04:45 PM UTC