Skip to content

Commit adc1f7e

Browse files
Fix silent overflow when coercing JSON numbers to integral types
1 parent 2bad41f commit adc1f7e

2 files changed

Lines changed: 47 additions & 1 deletion

File tree

java/src/org/openqa/selenium/json/JsonInput.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ public Number nextNumber() {
263263
return Long.valueOf(builder.toString());
264264
}
265265

266-
return new BigDecimal(builder.toString()).doubleValue();
266+
return new BigDecimal(builder.toString());
267267
} catch (NumberFormatException e) {
268268
throw new JsonException("Unable to parse to a number: " + builder + ". " + input, e);
269269
}

java/src/org/openqa/selenium/json/NumberCoercer.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
import java.io.StringReader;
2121
import java.lang.reflect.Type;
22+
import java.math.BigDecimal;
23+
import java.math.BigInteger;
2224
import java.util.Map;
2325
import java.util.function.BiFunction;
2426
import java.util.function.Function;
@@ -80,7 +82,51 @@ public BiFunction<JsonInput, PropertySetting, T> apply(Type ignored) {
8082
default:
8183
throw new JsonException("Unable to coerce to a number: " + jsonInput.peek());
8284
}
85+
validateIntegralRange(number, stereotype);
8386
return mapper.apply(number);
8487
};
8588
}
89+
90+
private static void validateIntegralRange(Number number, Class<?> stereotype) {
91+
// Only for integral targets
92+
if (!(stereotype == Byte.class
93+
|| stereotype == Short.class
94+
|| stereotype == Integer.class
95+
|| stereotype == Long.class)) {
96+
return;
97+
}
98+
99+
final BigInteger value;
100+
try {
101+
// Parses "2147483648", "1e3", "1.0" safely; rejects "1.2"
102+
BigDecimal bd =
103+
(number instanceof BigDecimal) ? (BigDecimal) number : new BigDecimal(number.toString());
104+
value = bd.toBigIntegerExact();
105+
} catch (RuntimeException e) {
106+
throw new JsonException(
107+
"Expected an integer value for " + stereotype.getSimpleName() + ": " + number, e);
108+
}
109+
110+
BigInteger min;
111+
BigInteger max;
112+
113+
if (stereotype == Byte.class) {
114+
min = BigInteger.valueOf(Byte.MIN_VALUE);
115+
max = BigInteger.valueOf(Byte.MAX_VALUE);
116+
} else if (stereotype == Short.class) {
117+
min = BigInteger.valueOf(Short.MIN_VALUE);
118+
max = BigInteger.valueOf(Short.MAX_VALUE);
119+
} else if (stereotype == Integer.class) {
120+
min = BigInteger.valueOf(Integer.MIN_VALUE);
121+
max = BigInteger.valueOf(Integer.MAX_VALUE);
122+
} else { // Long.class
123+
min = BigInteger.valueOf(Long.MIN_VALUE);
124+
max = BigInteger.valueOf(Long.MAX_VALUE);
125+
}
126+
127+
if (value.compareTo(min) < 0 || value.compareTo(max) > 0) {
128+
throw new JsonException(
129+
"Numeric value out of range for " + stereotype.getSimpleName() + ": " + value);
130+
}
131+
}
86132
}

0 commit comments

Comments
 (0)