authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-29 22:40:32-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-03-29 22:40:32-04:00
logb717df786bbf9b42abf1db1ec3fdae183debfdea
tree84320af40ef7ee890c1896aa51ec3319f9fe9c00
parentf9f7deaeda20b212ae4daea0009a904beccdf828
parent6809222d32e204ac46ee7b7b86a6ba5ebce955e6
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #4845 from xackus/fix-parseFloat

fix overflow in parseFloat and cleanup

2 files changed, 54 insertions(+), 76 deletions(-)

lib/std/fmt/parse_float.zig+51-71
...@@ -30,6 +30,7 @@...@@ -30,6 +30,7 @@
30// - Does not handle denormals30// - Does not handle denormals
3131
32const std = @import("../std.zig");32const std = @import("../std.zig");
33const ascii = std.ascii;
3334
34const max_digits = 25;35const max_digits = 25;
3536
...@@ -190,14 +191,6 @@ const ParseResult = enum {...@@ -190,14 +191,6 @@ const ParseResult = enum {
190 MinusInf,191 MinusInf,
191};192};
192193
193inline fn isDigit(c: u8) bool {
194 return c >= '0' and c <= '9';
195}
196
197inline fn isSpace(c: u8) bool {
198 return (c >= 0x09 and c <= 0x13) or c == 0x20;
199}
200
201fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult {194fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult {
202 var digit_index: usize = 0;195 var digit_index: usize = 0;
203 var negative = false;196 var negative = false;
...@@ -207,52 +200,49 @@ fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult {...@@ -207,52 +200,49 @@ fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult {
207 var state = State.MaybeSign;200 var state = State.MaybeSign;
208201
209 var i: usize = 0;202 var i: usize = 0;
210 loop: while (i < s.len) {203 while (i < s.len) {
211 const c = s[i];204 const c = s[i];
212205
213 switch (state) {206 switch (state) {
214 State.MaybeSign => {207 .MaybeSign => {
215 state = State.LeadingMantissaZeros;208 state = .LeadingMantissaZeros;
216209
217 if (c == '+') {210 if (c == '+') {
218 i += 1;211 i += 1;
219 } else if (c == '-') {212 } else if (c == '-') {
220 n.negative = true;213 n.negative = true;
221 i += 1;214 i += 1;
222 } else if (isDigit(c) or c == '.') {215 } else if (ascii.isDigit(c) or c == '.') {
223 // continue216 // continue
224 } else {217 } else {
225 return error.InvalidCharacter;218 return error.InvalidCharacter;
226 }219 }
227 },220 },
228221 .LeadingMantissaZeros => {
229 State.LeadingMantissaZeros => {
230 if (c == '0') {222 if (c == '0') {
231 i += 1;223 i += 1;
232 } else if (c == '.') {224 } else if (c == '.') {
233 i += 1;225 i += 1;
234 state = State.LeadingFractionalZeros;226 state = .LeadingFractionalZeros;
235 } else {227 } else {
236 state = State.MantissaIntegral;228 state = .MantissaIntegral;
237 }229 }
238 },230 },
239231 .LeadingFractionalZeros => {
240 State.LeadingFractionalZeros => {
241 if (c == '0') {232 if (c == '0') {
242 i += 1;233 i += 1;
243 if (n.exponent > std.math.minInt(i32)) {234 if (n.exponent > std.math.minInt(i32)) {
244 n.exponent -= 1;235 n.exponent -= 1;
245 }236 }
246 } else {237 } else {
247 state = State.MantissaFractional;238 state = .MantissaFractional;
248 }239 }
249 },240 },
250241 .MantissaIntegral => {
251 State.MantissaIntegral => {242 if (ascii.isDigit(c)) {
252 if (isDigit(c)) {
253 if (digit_index < max_digits) {243 if (digit_index < max_digits) {
254 n.mantissa *%= 10;244 n.mantissa *%= 10;
255 n.mantissa += s[i] - '0';245 n.mantissa += c - '0';
256 digit_index += 1;246 digit_index += 1;
257 } else if (n.exponent < std.math.maxInt(i32)) {247 } else if (n.exponent < std.math.maxInt(i32)) {
258 n.exponent += 1;248 n.exponent += 1;
...@@ -261,14 +251,13 @@ fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult {...@@ -261,14 +251,13 @@ fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult {
261 i += 1;251 i += 1;
262 } else if (c == '.') {252 } else if (c == '.') {
263 i += 1;253 i += 1;
264 state = State.MantissaFractional;254 state = .MantissaFractional;
265 } else {255 } else {
266 state = State.MantissaFractional;256 state = .MantissaFractional;
267 }257 }
268 },258 },
269259 .MantissaFractional => {
270 State.MantissaFractional => {260 if (ascii.isDigit(c)) {
271 if (isDigit(c)) {
272 if (digit_index < max_digits) {261 if (digit_index < max_digits) {
273 n.mantissa *%= 10;262 n.mantissa *%= 10;
274 n.mantissa += c - '0';263 n.mantissa += c - '0';
...@@ -279,13 +268,12 @@ fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult {...@@ -279,13 +268,12 @@ fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult {
279 i += 1;268 i += 1;
280 } else if (c == 'e' or c == 'E') {269 } else if (c == 'e' or c == 'E') {
281 i += 1;270 i += 1;
282 state = State.ExponentSign;271 state = .ExponentSign;
283 } else {272 } else {
284 state = State.ExponentSign;273 state = .ExponentSign;
285 }274 }
286 },275 },
287276 .ExponentSign => {
288 State.ExponentSign => {
289 if (c == '+') {277 if (c == '+') {
290 i += 1;278 i += 1;
291 } else if (c == '-') {279 } else if (c == '-') {
...@@ -293,20 +281,18 @@ fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult {...@@ -293,20 +281,18 @@ fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult {
293 i += 1;281 i += 1;
294 }282 }
295283
296 state = State.LeadingExponentZeros;284 state = .LeadingExponentZeros;
297 },285 },
298286 .LeadingExponentZeros => {
299 State.LeadingExponentZeros => {
300 if (c == '0') {287 if (c == '0') {
301 i += 1;288 i += 1;
302 } else {289 } else {
303 state = State.Exponent;290 state = .Exponent;
304 }291 }
305 },292 },
306293 .Exponent => {
307 State.Exponent => {294 if (ascii.isDigit(c)) {
308 if (isDigit(c)) {295 if (exponent < std.math.maxInt(i32) / 10) {
309 if (exponent < std.math.maxInt(i32)) {
310 exponent *= 10;296 exponent *= 10;
311 exponent += @intCast(i32, c - '0');297 exponent += @intCast(i32, c - '0');
312 }298 }
...@@ -323,29 +309,21 @@ fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult {...@@ -323,29 +309,21 @@ fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult {
323 n.exponent += exponent;309 n.exponent += exponent;
324310
325 if (n.mantissa == 0) {311 if (n.mantissa == 0) {
326 return if (n.negative) ParseResult.MinusZero else ParseResult.PlusZero;312 return if (n.negative) .MinusZero else .PlusZero;
327 } else if (n.exponent > 309) {313 } else if (n.exponent > 309) {
328 return if (n.negative) ParseResult.MinusInf else ParseResult.PlusInf;314 return if (n.negative) .MinusInf else .PlusInf;
329 } else if (n.exponent < -328) {315 } else if (n.exponent < -328) {
330 return if (n.negative) ParseResult.MinusZero else ParseResult.PlusZero;316 return if (n.negative) .MinusZero else .PlusZero;
331 }317 }
332318
333 return ParseResult.Ok;319 return .Ok;
334}
335
336inline fn isLower(c: u8) bool {
337 return c -% 'a' < 26;
338}
339
340inline fn toUpper(c: u8) u8 {
341 return if (isLower(c)) (c & 0x5f) else c;
342}320}
343321
344fn caseInEql(a: []const u8, b: []const u8) bool {322fn caseInEql(a: []const u8, b: []const u8) bool {
345 if (a.len != b.len) return false;323 if (a.len != b.len) return false;
346324
347 for (a) |_, i| {325 for (a) |_, i| {
348 if (toUpper(a[i]) != toUpper(b[i])) {326 if (ascii.toUpper(a[i]) != ascii.toUpper(b[i])) {
349 return false;327 return false;
350 }328 }
351 }329 }
...@@ -373,11 +351,11 @@ pub fn parseFloat(comptime T: type, s: []const u8) !T {...@@ -373,11 +351,11 @@ pub fn parseFloat(comptime T: type, s: []const u8) !T {
373 };351 };
374352
375 return switch (try parseRepr(s, &r)) {353 return switch (try parseRepr(s, &r)) {
376 ParseResult.Ok => convertRepr(T, r),354 .Ok => convertRepr(T, r),
377 ParseResult.PlusZero => 0.0,355 .PlusZero => 0.0,
378 ParseResult.MinusZero => -@as(T, 0.0),356 .MinusZero => -@as(T, 0.0),
379 ParseResult.PlusInf => std.math.inf(T),357 .PlusInf => std.math.inf(T),
380 ParseResult.MinusInf => -std.math.inf(T),358 .MinusInf => -std.math.inf(T),
381 };359 };
382}360}
383361
...@@ -396,26 +374,28 @@ test "fmt.parseFloat" {...@@ -396,26 +374,28 @@ test "fmt.parseFloat" {
396 testing.expectError(error.InvalidCharacter, parseFloat(T, "1abc"));374 testing.expectError(error.InvalidCharacter, parseFloat(T, "1abc"));
397375
398 expectEqual(try parseFloat(T, "0"), 0.0);376 expectEqual(try parseFloat(T, "0"), 0.0);
399 expectEqual((try parseFloat(T, "0")), 0.0);377 expectEqual(try parseFloat(T, "0"), 0.0);
400 expectEqual((try parseFloat(T, "+0")), 0.0);378 expectEqual(try parseFloat(T, "+0"), 0.0);
401 expectEqual((try parseFloat(T, "-0")), 0.0);379 expectEqual(try parseFloat(T, "-0"), 0.0);
402380
403 expectEqual((try parseFloat(T, "0e0")), 0);381 expectEqual(try parseFloat(T, "0e0"), 0);
404 expectEqual((try parseFloat(T, "2e3")), 2000.0);382 expectEqual(try parseFloat(T, "2e3"), 2000.0);
405 expectEqual((try parseFloat(T, "1e0")), 1.0);383 expectEqual(try parseFloat(T, "1e0"), 1.0);
406 expectEqual((try parseFloat(T, "-2e3")), -2000.0);384 expectEqual(try parseFloat(T, "-2e3"), -2000.0);
407 expectEqual((try parseFloat(T, "-1e0")), -1.0);385 expectEqual(try parseFloat(T, "-1e0"), -1.0);
408 expectEqual((try parseFloat(T, "1.234e3")), 1234);386 expectEqual(try parseFloat(T, "1.234e3"), 1234);
409387
410 expect(approxEq(T, try parseFloat(T, "3.141"), 3.141, epsilon));388 expect(approxEq(T, try parseFloat(T, "3.141"), 3.141, epsilon));
411 expect(approxEq(T, try parseFloat(T, "-3.141"), -3.141, epsilon));389 expect(approxEq(T, try parseFloat(T, "-3.141"), -3.141, epsilon));
412390
413 expectEqual((try parseFloat(T, "1e-700")), 0);391 expectEqual(try parseFloat(T, "1e-700"), 0);
414 expectEqual((try parseFloat(T, "1e+700")), std.math.inf(T));392 expectEqual(try parseFloat(T, "1e+700"), std.math.inf(T));
415393
416 expectEqual(@bitCast(Z, try parseFloat(T, "nAn")), @bitCast(Z, std.math.nan(T)));394 expectEqual(@bitCast(Z, try parseFloat(T, "nAn")), @bitCast(Z, std.math.nan(T)));
417 expectEqual((try parseFloat(T, "inF")), std.math.inf(T));395 expectEqual(try parseFloat(T, "inF"), std.math.inf(T));
418 expectEqual((try parseFloat(T, "-INF")), -std.math.inf(T));396 expectEqual(try parseFloat(T, "-INF"), -std.math.inf(T));
397
398 expectEqual(try parseFloat(T, "0.4e0066999999999999999999999999999999999999999999999999999"), std.math.inf(T));
419399
420 if (T != f16) {400 if (T != f16) {
421 expect(approxEq(T, try parseFloat(T, "1e-2"), 0.01, epsilon));401 expect(approxEq(T, try parseFloat(T, "1e-2"), 0.01, epsilon));
lib/std/json/test.zig+3-5
...@@ -1751,11 +1751,9 @@ test "i_number_double_huge_neg_exp" {...@@ -1751,11 +1751,9 @@ test "i_number_double_huge_neg_exp" {
1751}1751}
17521752
1753test "i_number_huge_exp" {1753test "i_number_huge_exp" {
1754 return error.SkipZigTest;1754 any(
1755 // FIXME Integer overflow in parseFloat1755 \\[0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006]
1756 // any(1756 );
1757 // \\[0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006]
1758 // );
1759}1757}
17601758
1761test "i_number_neg_int_huge_exp" {1759test "i_number_neg_int_huge_exp" {