Skip to content

[2.x] Fix silent numeric overflow in argument and input binding - #2782

Open
RoMiRoSSaN wants to merge 2 commits into
smallrye:2.xfrom
RoMiRoSSaN:number_fix_2x
Open

RoMiRoSSaN wants to merge 2 commits into
smallrye:2.xfrom
RoMiRoSSaN:number_fix_2x

Conversation

@RoMiRoSSaN

Copy link
Copy Markdown
Contributor

Fixes #2781

Changes

  • IntegerScalar: the range check is exact and includes both bounds. Int.MIN_VALUE is accepted.
  • Integer targets (byte, short, int, long, atomic and Optional* wrappers): the conversion is exact. intValue() and casts are replaced with *ValueExact, so overflow and fractions return a GraphQL error.
  • Floating-point targets (float, double, OptionalDouble): NaN, Infinity and finite overflow return a GraphQL error. Normal IEEE rounding is kept.
  • Input objects (records, POJOs, nested objects, lists): numeric fields are checked before JSON binding. Each field's number type is resolved once, when the schema is built.
  • Missing OptionalInt / OptionalLong / OptionalDouble arguments: passed as Optional*.empty() instead of null.
  • Unchanged: BigInteger, BigDecimal and the schema.
// before
return bigInteger.intValue();
return (byte) input.intValue();

// after
return bigInteger.intValueExact();
return input instanceof Byte ? input : byteValueExact(input);

Before / after

Java parameter Input Before After
int 2147483648 method receives -2147483648 error
int -2147483648 error method receives -2147483648
int variable 1.5 method receives 1 error
byte 128 method receives -128 error
long variable 9223372036854775808 method receives -9223372036854775808 error
float 1e39 method receives Infinity error
record with int field -99999999999999999999999999999 method receives 1610612737, or an unrelated Unknown Scalar Type error error naming the field

Example error after the fix:

Validation error (WrongType@[primitiveInt]) : argument 'input' with value 'IntValue{value=2147483648}'
is not a valid 'Int' - SRGQL000014: Expected value to be in the Int range but it was 'IntValue{value=2147483648}'

Reject out-of-range numbers instead of silently wrapping them, and accept
Int.MIN_VALUE. Narrowing to Java numeric types is exact, including fields
of input objects; overflow, fractions and non-finite floats are errors.
Add exact JSON-B AtomicInteger/AtomicLong serializers and deserializers.

Preserve converted numeric default values in SDL and introspection.
Distinguish fractional Int literals from values outside the Int range.

Cover numeric bindings, literal conversion, default values and diagnostics
with regression tests.

@phillip-kruger phillip-kruger left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The FieldHelper.getArrayType change breaks @NumberFormat on numeric array outputs. See the inline comment. The rest of server/implementation passes locally.

Two behaviour changes that aren't in the description and should go in the release notes:

  • Output serialization also goes through the new convertImpl, so a resolver returning NaN or Infinity for a Float field now gets a field error instead of passing the value through.
  • A missing OptionalInt/OptionalLong/OptionalDouble argument is now empty() instead of null.


@Override
protected Class<?> getArrayType(final Field field) {
if (Classes.isNumberLikeType(field.getReference().getClassName())) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This breaks @NumberFormat on arrays. For a formatted number the reference keeps className = the number type but graphQLClassName = String. This branch returns the number class, so recursiveTransformArray hits the "component type already matches" shortcut and never applies the format:

@Query @NumberFormat("#,##0.00") public Double[] boxedDoubles() { return new Double[]{1234.5, 2.0}; }
@Query @NumberFormat("#,##0.00") public int[] primitiveInts() { return new int[]{1234, 2}; }

2.x returns ["1,234.50","2.00"] / ["1,234.00","2.00"]. With this PR it returns ["1234.5","2.0"] / ["1234","2"]. Please skip this branch when field.hasTransformation() and add a test for formatted arrays.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Skip the numeric component shortcut when a field has a transformation so
that NumberFormat still formats each array element as a string.

Cover primitive and boxed arrays, arrays inside lists, and null elements.
Characterize Optional argument, POJO field, and record component binding
without changing the existing Optional contract.
@jmartisk jmartisk changed the title Fix silent numeric overflow in argument and input binding [2.x] Fix silent numeric overflow in argument and input binding Nov 13, 2026
@RoMiRoSSaN

Copy link
Copy Markdown
Contributor Author

@phillip-kruger Thanks for pointing that out. I missed it. I've fixed it and added more tests. Looking forward to your further feedback. I've applied the fixes to both pull requests. In the meantime, I'll also test the fixes in my projects using my local fork.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants