diff --git a/changelogs/unreleased/gh-10164-yaml-encoding-large-exps.md b/changelogs/unreleased/gh-10164-yaml-encoding-large-exps.md new file mode 100644 index 0000000000000000000000000000000000000000..4901695fb28ff1bc6f400f3c17efaac636593d44 --- /dev/null +++ b/changelogs/unreleased/gh-10164-yaml-encoding-large-exps.md @@ -0,0 +1,4 @@ +## bugfix/lua/yaml + +* Strings with large exponential values equal to infinity are now encoded as + strings instead of numbers (gh-10164). diff --git a/test/app-tap/lua/serializer_test.lua b/test/app-tap/lua/serializer_test.lua index 0a99d420fdf28e113fcbdf2ac75f4d266dd9f1d9..2b808a5c98f67a7562d920e6c3f5fe149599bd3f 100644 --- a/test/app-tap/lua/serializer_test.lua +++ b/test/app-tap/lua/serializer_test.lua @@ -236,7 +236,7 @@ local function test_boolean(test, s) end local function test_string(test, s) - test:plan(8) + test:plan(11) rt(test, s, "") rt(test, s, "abcde") rt(test, s, "Кудыкины горы") -- utf-8 @@ -245,6 +245,9 @@ local function test_string(test, s) rt(test, s, '$a\t $') rt(test, s, [[$a\t $]]) rt(test, s, [[$a\\t $]]) + rt(test, s, '9e123456789') + rt(test, s, 'infinity') + rt(test, s, 'NaN') end local function test_nil(test, s) diff --git a/third_party/lua-yaml/lyaml.cc b/third_party/lua-yaml/lyaml.cc index ed0fca40ae81538d8542f48af22596cbe5d3c129..3d3249d0c8f0c4f78239ca5ca68986b7726334c3 100644 --- a/third_party/lua-yaml/lyaml.cc +++ b/third_party/lua-yaml/lyaml.cc @@ -159,6 +159,54 @@ yaml_is_null(const char *str, size_t len) return false; } +/** + * Verify whether a string represents a number literal in YAML. + * + * Non-standard: + * + * False-positives: + * - 'inf', 'nan' literals despite the case are parsed as numbers + * (the standard specifies only 'inf', 'Inf', 'INF', 'nan', + * 'NaN', 'NAN'). + * - 'infinity' (ignoring case) is considered a number. + * - Binary literals ('0b...') are considered numbers. + * + * Bugs: + * - Octal numbers are not supported. + * + * This function is used only in encoding for wrapping strings + * containing number literals in quotes to make YAML parser + * handle them as strings. It means false-positives will lead to + * extra quotation marks and are not dangerous at all. + * + * @param str Literal to check. + * @param len Length of @a str. + * + * @retval Whether @a str represents a number value. + */ +static inline bool +yaml_is_number(const char *str, size_t len, struct lua_State *L) +{ + /* + * TODO: Should be implemented with the literal parser + * instead of using strtod() and lua_isnumber(). + * Using parser will make it possible to remove the third + * argument. + */ + if (len == 0) + return false; + + if (lua_isnumber(L, -1)) + return true; + + char *endptr = NULL; + fpconv_strtod(str, &endptr); + if (endptr == str + len) + return true; + + return false; +} + static void generate_error_message(struct lua_yaml_loader *loader) { char buf[256]; luaL_Buffer b; @@ -668,7 +716,7 @@ static int dump_node(struct lua_yaml_dumper *dumper) case MP_STR: str = lua_tolstring(dumper->L, -1, &len); if (yaml_is_null(str, len) || yaml_is_bool(str, len, &unused) || - lua_isnumber(dumper->L, -1)) { + yaml_is_number(str, len, dumper->L)) { /* * The string is convertible to a null, a boolean or * a number, quote it to preserve its type.