pjfanning opened a new pull request, #98:
URL: https://github.com/apache/poi-xmlbeans/pull/98
Follow-up to #97. That PR hardened the paths that *consume* an oversized
decimal; this one stops one being created from a document in the first place.
### The gap
`maxNumberOfCharsForNumbers` is documented as bounding the size of numbers
read out of a document, but it only checked the length of the lexical value. An
exponent is a handful of characters denoting arbitrarily many digits:
```
parse <xml-fragment>1E+2000000000</xml-fragment> as XmlDecimal, default
options
-> stored, scale=-2000000000 precision=1
```
13 characters, under any limit, producing a `BigDecimal` that is trivial to
hold and catastrophic to expand. No option is needed to get there:
`Factory.parse` doesn't validate on set, so `validateLexical` — the check that
rejects an exponent — never runs. `setLoadAllowDecimalExponent(true)` reaches
it by the documented route.
### Why the check is safe
Exponent notation isn't part of the `xsd:decimal` lexical space at all; it
belongs to `float`/`double`. XSD leaves the value space unbounded and only
requires that a minimally conforming processor support at least 18 digits (Part
2 §3.2.3), so an implementation limit well above that is conformant either way.
The consequence that matters here: for conformant input the digit count is
bounded by the length of the lexical value, so a bound on digits can never
reject something the existing length check accepts. It fires only for exponent
forms that were never valid `xsd:decimal` — which is why
`testParseAsBigDecimalAcceptsPlainValues` can assert the plain forms are
untouched.
### The change
Applied in `MathUtil.parseAsBigDecimal`, the single point that both
`JavaDecimalHolder.set_text` and `JavaDecimalHolderEx.set_text` go through,
along with `validateLexical`'s exponent branch and `Validator`. The bound is
the length of the value written out without an exponent, which is the form
`xsd:decimal` allows and therefore the form the limit is meant to bound:
```java
return scale <= 0
? (long) precision - scale // integer digits
: Math.max(precision, (long) scale + 1) + 1; // digits either side
of the point
```
Both directions are covered — `1E-2000000000` would otherwise make
`printDecimal` allocate a two-billion-character `StringBuilder`.
A raised limit still raises the bound: `1E+2000` is rejected at the default
1024 and accepted at `maxChars(4096)`.
### Still open
`setBigDecimalValue` remains unbounded, so a value like this can still be
constructed programmatically. That's why #97's guards in `value_hash_code` and
`to_BigInteger` stay: they now defend a path that a document can no longer
reach, rather than the one it could.
### Tests
Two in `TestMathUtil` (the bound applies to denoted digits, in both exponent
directions and under raised and lowered limits; plain lexical forms are
unaffected) and two in `MaxNumberOfCharsTest` (a document is rejected with and
without the exponent option, and an exponent within the limit still parses).
Full suite: 3086 tests, 0 failures.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]