[GH-ISSUE #8113] [Bug] XML plist decode preflight underestimates decoder memory by 2–4× #4676

Closed
opened 2026-10-31 00:52:20 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @kaoru0822-kitauji on GitHub (Oct 30, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/8113

Originally assigned to: @kaoru0822-kitauji on GitHub.

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Native analysis or decompilation

Observed behavior

inspect-plist reserves a 64 MiB decode budget before it decodes an XML plist. The estimate is much smaller than the memory the XML decoder actually uses, so XML plists whose decode peaks at 2–4× the budget are admitted and decoded.

A 1.75 MB XML plist made of 230,000 <true/> lines decodes successfully. The CLI process then peaks at 575 MiB RSS, against 236 MiB for a small Info.plist. Real Apple files are affected too: Keynote.app/Contents/Resources/KnownFonts.plist and AudioVideoBridging.framework/.../oui.plist decode today although their decode peak exceeds 64 MiB.

Reproduction

At origin/main 317bf3fe2:

python3 -c "open('dom.plist','w').write('<?xml version=\"1.0\"?>\n<plist version=\"1.0\">\n<array>\n' + '<true/>\n'*230000 + '</array>\n</plist>\n')"
/usr/bin/time -l node scripts/rea.mjs inspect-plist "$PWD/dom.plist" --format json > /dev/null

Result: exit 0 and a maximum resident set size of about 575 MiB. MCP inspect_plist (after open_binary) uses the same provider path. It reproduced on every run I made.

Expected behavior

The preflight charge should be an upper bound on the decoder's peak heap, and these inputs should be refused with resource_constraint before decoding.

Impact and scope

The 64 MiB XML decode budget does not bound decode memory. A plist made of many small elements, whether hostile or simply large, can make the analysis process use several times the allowance. Binary plists are not affected. The binary estimate measured above the real decode peak on every shape I tried; for example, Journal's Localizable.loctable is charged 86.7 MiB against a peak of about 54 MiB of heap plus about 7 MiB of input copies. Interface Builder XML documents use the same estimator under their aggregate budget.

REA version

6.8.0 (source origin/main 317bf3fe2)

Runtime and platform

Node.js v26.5.0, macOS 26.7.1, arm64

Target and artifact context

XML property lists, both synthetic and shipped in /Applications and /System/Library

Interface

CLI

Earliest failure stage

Request parsing or validation

Relevant output or evidence

I measured the decode peak as the smallest --max-old-space-size at which parsePlistXmlSource succeeds with the budget lifted, minus the 11 MiB a baseline process needs. Charges marked ≈ follow the current formula, computed outside REA:

XML input Size Current charge Measured decode peak
230,000 <true/> lines 1.75 MB 63.2 MiB 249 MiB
230,000 <true/> 1.6 MB 62.3 MiB 222 MiB
115,000 <integer>1</integer> 2.3 MB 64.9 MiB 144 MiB
230,000 comments 1.6 MB 6.1 MiB 44 MiB
KnownFonts.plist (Keynote) 1.6 MB ≈35 MiB 80 MiB
oui.plist (AudioVideoBridging) 2.2 MB ≈43 MiB 104 MiB

Typed outcome or recovery expected

resource_constraint with maximum_decoded_bytes for inputs whose decode would exceed the allowance.

Analysis or suspected cause

Source-derived, consistent with the measurements: estimateXmlPlistExpansion in src/artifacts/apple/PropertyListDecodeBudget.ts charges 4 bytes per character plus 256 bytes per start or end tag. The decoder builds an @xmldom/xmldom DOM, which measured about 1,000–1,160 bytes per element and up to about 240 bytes per text, comment or CDATA node. Comments, CDATA sections and text nodes are not charged at all.

Additional context

Open PR #7166 changes the binary estimator in the same file for a different cause (keyed-archive dates).

Originally created by @kaoru0822-kitauji on GitHub (Oct 30, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/8113 Originally assigned to: @kaoru0822-kitauji on GitHub. ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Native analysis or decompilation ### Observed behavior `inspect-plist` reserves a 64 MiB decode budget before it decodes an XML plist. The estimate is much smaller than the memory the XML decoder actually uses, so XML plists whose decode peaks at 2–4× the budget are admitted and decoded. A 1.75 MB XML plist made of 230,000 `<true/>` lines decodes successfully. The CLI process then peaks at **575 MiB RSS**, against 236 MiB for a small Info.plist. Real Apple files are affected too: `Keynote.app/Contents/Resources/KnownFonts.plist` and `AudioVideoBridging.framework/.../oui.plist` decode today although their decode peak exceeds 64 MiB. ### Reproduction At `origin/main` 317bf3fe2: ```sh python3 -c "open('dom.plist','w').write('<?xml version=\"1.0\"?>\n<plist version=\"1.0\">\n<array>\n' + '<true/>\n'*230000 + '</array>\n</plist>\n')" /usr/bin/time -l node scripts/rea.mjs inspect-plist "$PWD/dom.plist" --format json > /dev/null ``` Result: exit 0 and a maximum resident set size of about 575 MiB. MCP `inspect_plist` (after `open_binary`) uses the same provider path. It reproduced on every run I made. ### Expected behavior The preflight charge should be an upper bound on the decoder's peak heap, and these inputs should be refused with `resource_constraint` before decoding. ### Impact and scope The 64 MiB XML decode budget does not bound decode memory. A plist made of many small elements, whether hostile or simply large, can make the analysis process use several times the allowance. Binary plists are not affected. The binary estimate measured above the real decode peak on every shape I tried; for example, Journal's `Localizable.loctable` is charged 86.7 MiB against a peak of about 54 MiB of heap plus about 7 MiB of input copies. Interface Builder XML documents use the same estimator under their aggregate budget. ### REA version 6.8.0 (source `origin/main` 317bf3fe2) ### Runtime and platform Node.js v26.5.0, macOS 26.7.1, arm64 ### Target and artifact context XML property lists, both synthetic and shipped in `/Applications` and `/System/Library` ### Interface CLI ### Earliest failure stage Request parsing or validation ### Relevant output or evidence I measured the decode peak as the smallest `--max-old-space-size` at which `parsePlistXmlSource` succeeds with the budget lifted, minus the 11 MiB a baseline process needs. Charges marked ≈ follow the current formula, computed outside REA: | XML input | Size | Current charge | Measured decode peak | | --- | ---: | ---: | ---: | | 230,000 `<true/>` lines | 1.75 MB | 63.2 MiB | 249 MiB | | 230,000 `<true/>` | 1.6 MB | 62.3 MiB | 222 MiB | | 115,000 `<integer>1</integer>` | 2.3 MB | 64.9 MiB | 144 MiB | | 230,000 comments | 1.6 MB | 6.1 MiB | 44 MiB | | `KnownFonts.plist` (Keynote) | 1.6 MB | ≈35 MiB | 80 MiB | | `oui.plist` (AudioVideoBridging) | 2.2 MB | ≈43 MiB | 104 MiB | ### Typed outcome or recovery expected `resource_constraint` with `maximum_decoded_bytes` for inputs whose decode would exceed the allowance. ### Analysis or suspected cause Source-derived, consistent with the measurements: `estimateXmlPlistExpansion` in `src/artifacts/apple/PropertyListDecodeBudget.ts` charges 4 bytes per character plus 256 bytes per start or end tag. The decoder builds an `@xmldom/xmldom` DOM, which measured about 1,000–1,160 bytes per element and up to about 240 bytes per text, comment or CDATA node. Comments, CDATA sections and text nodes are not charged at all. ### Additional context Open PR #7166 changes the binary estimator in the same file for a different cause (keyed-archive dates).
bsakaguchi 2026-10-31 00:52:20 +00:00
  • closed this issue
  • added the
    bug
    label
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#4676
No description provided.