Skip to content

bug: int(), uint(), float() on a string and json.parse number fields saturate, wrap, or accept trailing garbage instead of failing #2789

Description

@SchoolyB

The string-to-number conversions int(s), uint(s), float(s), and the int, uint, and float fields of a #json struct (json.parse) all go through the same two runtime functions, gray_builtin_string_to_int and gray_builtin_string_to_float. They call strtoll / strtod without checking errno, accept whitespace, and copy the input into a 64-byte stack buffer. STANDARD.md §3.4 says "Conversions that would lose information or are invalid produce check-time or runtime errors", and strconv.to_int / to_uint / to_float already reject each case below.

Input Actual Expected
int("9223372036854775808") 9223372036854775807 panic P0084
int("-99999999999999999999") -9223372036854775808 panic P0084
uint("18446744073709551615") 9223372036854775807 18446744073709551615
uint("18446744073709551616") 9223372036854775807 panic
uint("-1") 18446744073709551615 panic
float("1e999") inf panic P0085
int(" 5"), int("5 ") 5 panic
int("42 apples") 42 panic
float("2.5 kg") 2.5 panic
float("0x10") 16.0 panic (while int("0x10") panics)
int("0" * 400 + "5") 0 5
int("0" * 400 + "5x") 0 panic
float("7" + "0" * 400 + "x") 7e+62 panic (the value is 7e400, which overflows)

The last three rows come from the 64-byte buffer: anything past byte 63 is silently dropped, so trailing garbage is never seen and a long value is silently cut. The whitespace rows come from end being allowed to stop at a space.

json.parse into a #json struct uses the same functions, so a JSON document decodes to wrong values with no error:

JSON Field type Actual
{"a": 18446744073709551615} uint 9223372036854775807
{"a": 99999999999999999999} int 9223372036854775807
{"a": -1} uint 18446744073709551615

Expected

Each row above panics (P0084 / P0085; malformed field values in json.parse are a json.parse failure per §7.5.2), or returns the exact value when the input is valid, the same as strconv.to_int / to_uint / to_float.

Actual

The values in the tables, with no diagnostic.

Reproduction

import @json
import @strings

#json
const T struct {
    a uint
}

do main() {
    println(int("9223372036854775808"))                       // 9223372036854775807, expected panic
    println(uint("-1"))                                       // 18446744073709551615, expected panic
    println(float("1e999"))                                   // inf, expected panic
    println(int("42 apples"))                                 // 42, expected panic
    println(int(strings.repeat("0", 400) + "5"))              // 0, expected 5

    mut t T = json.parse("{\"a\": 18446744073709551615}")
    println(t.a)                                              // 9223372036854775807, expected 18446744073709551615
}

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingstdlibGeneral standard library issues

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions