CNA-BUG-017: XnbReadLimits::maxFileSize is not applied to ContentManager's whole-file .xnb reads
Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.
The 64 MiB maxFileSize ceiling is enforced by the decompressors and the build-time canonical read, but ContentManager reads a run-time .xnb of up to INT32_MAX bytes and the manifest scan reads any size.
- Identifier
CNA-BUG-017- Category
- Bug
- Subsystem
- Content & XNB/CNB/CNJ
- Status
- Open
- Verified against
- CNA
009d40f5(009d40f5dd085c4e674d3479675fac84b12b3e0a) - Severity
- Low (a triage suggestion, not a project priority)
- Evidence basis
- Source-verified: read at TARGET, not executed
- Tests touching this area
- Yes: see Current tests
- Affected contract
- CNA::Internal::Xnb::XnbReadLimits::maxFileSize ("Largest .xnb file this reader will attempt to open, in bytes") as applied by ContentManager::Load
Expected behaviour
An .xnb larger than maxFileSize (64 MiB by default) is refused with ContentLoadException before its bytes are read into memory, as the field's documentation in XnbReadLimits.hpp says and as CnbDocument::Parse does for CnbReadLimits::maxFileSize.
Actual behaviour at TARGET
ContentManager::TryReadAssetBytes (ContentManager.cpp) reads the whole file after checking only that it fits in INT32_MAX; LoadXnbAsset<T>, LoadXnbAssetUntyped and ReadStreamToEnd (the ReadAsset<T> route) check the same bound or none. ScanXnbReaderNames, run by RefreshContentManifest, reads every .xnb in the content root into a std::string with no size check at all. The only consumers of maxFileSize are the compressed-payload checks in XnbDecompression.cpp (LZX and LZ4) and the build-time canonical source read (ReadFile in XnbCanonicalData.cpp). An uncompressed .xnb between 64 MiB and 2 GiB is therefore read in full, up to a ~2 GiB allocation, before any other validation runs. The REMED-CONTENT-006 comment in XnbTypeReaderTable.hpp relies on a "coarse whole-stream limit (up to limits.maxFileSize)" that the run-time path does not have.
Source locations
modules/content/src/Xna/ContentManager.cpp— ContentManager::TryReadAssetBytes, ReadStreamToEnd and ScanXnbReaderNames - whole-file readsmodules/content/include/Microsoft/Xna/Framework/Content/ContentManager.hpp— ContentManager::LoadXnbAsset - INT32_MAX check onlymodules/content/include/CNA/Internal/Xnb/XnbReadLimits.hpp— XnbReadLimits::maxFileSizemodules/content/src/Xnb/XnbDecompression.cpp— DecompressXnbPayload / DecompressXnbLz4Payload - the run-time consumers
Evidence
Checked by reading at 009d40f5; not executed. The site's content-runtime internals page already records this ("an uncompressed .xnb between 64 MiB and 2 GiB is not refused by the file-size limit at run time"). The CNB path is not affected: CnbDocument::Parse checks its 512 MiB limit.
Focused reproduction
No focused reproduction is known. Nothing has been invented here; the evidence above is what exists.
Current tests
No test drives an .xnb above 64 MiB through ContentManager. Lz4DecoderTests.cpp exercises maxFileSize only on the decompressor; CnbSpecConformanceTests.cpp pins the separate CNB limit.
Regression test
Create a sparse file just over 64 MiB with a valid uncompressed XNB header (std::filesystem::resize_file keeps it cheap) and require Load<T> to throw ContentLoadException naming the size limit, without reading the body. A second case for GetContentManifest() should leave that entry's reader list empty without reading the file.
Blast radius
The .xnb tier of Load<T>, ReadAsset<T> through the base OpenStream, untyped external references, and the content manifest scan. Compressed .xnb payloads and all .cnb files are bounded as documented.
Workaround
No workaround is known.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- Known issues
- Bug index