diff --git a/cmake/BuildLua.cmake b/cmake/BuildLua.cmake index 97186f6..0882104 100644 --- a/cmake/BuildLua.cmake +++ b/cmake/BuildLua.cmake @@ -64,6 +64,10 @@ macro(build_lua LUA_VERSION) set(CFLAGS "${CFLAGS} -DLUA_USE_DLOPEN") endif() + # `io.popen()` is not supported by default, it is enabled + # by `LUA_USE_POSIX` flag. Required by a function `random_locale()`. + set(CFLAGS "${CFLAGS} -DLUA_USE_POSIX") + include(ExternalProject) set(LUA_LIBRARY ${PROJECT_BINARY_DIR}/lua-${LUA_VERSION}/source/liblua.a) diff --git a/tests/lapi/lib.lua b/tests/lapi/lib.lua index 09933d4..9c95a0f 100644 --- a/tests/lapi/lib.lua +++ b/tests/lapi/lib.lua @@ -55,6 +55,8 @@ local MIN_INT64 = math.mininteger or -0x8000000000000000 local MAX_INT = 0x7fffffff local MIN_INT = -0x80000000 +local MAX_STR_LEN = 4096 + local function bitwise_op(op_name) return function(...) local n = select("#", ...) @@ -84,6 +86,16 @@ local function approx_equal(a, b, epsilon) return abs(a - b) <= ((abs(a) < abs(b) and abs(b) or abs(a)) * epsilon) end +local function random_locale(fdp) + local locales = {} + local locale_it = io.popen("locale -a"):read("*a"):gmatch("([^\n]*)\n?") + for locale in locale_it do + table.insert(locales, locale) + end + + return fdp:oneof(locales) +end + return { approx_equal = approx_equal, bitwise_op = bitwise_op, @@ -95,4 +107,8 @@ return { MIN_INT64 = MIN_INT64, MAX_INT = MAX_INT, MIN_INT = MIN_INT, + MAX_STR_LEN = MAX_STR_LEN, + + -- FDP. + random_locale = random_locale, } diff --git a/tests/lapi/string_byte_test.lua b/tests/lapi/string_byte_test.lua new file mode 100644 index 0000000..e309f72 --- /dev/null +++ b/tests/lapi/string_byte_test.lua @@ -0,0 +1,37 @@ +--[[ +SPDX-License-Identifier: ISC +Copyright (c) 2023-2025, Sergey Bronnikov. + +6.4 – String Manipulation +https://www.lua.org/manual/5.3/manual.html#6.4 + +string.byte gets confused with some out-of-range negative indices, +https://www.lua.org/bugs.html#5.1.3-9 +]] + +-- Synopsis: string.byte(s [, i [, j]]) + +local luzer = require("luzer") +local test_lib = require("lib") + +local function TestOneInput(buf, _size) + local fdp = luzer.FuzzedDataProvider(buf) + os.setlocale(test_lib.random_locale(fdp), "all") + local str = fdp:consume_string(test_lib.MAX_STR_LEN) + local i = fdp:consume_integer(0, test_lib.MAX_INT) + local j = fdp:consume_integer(0, test_lib.MAX_INT) + -- `string.byte()` is the same as `str:byte()`. + assert(string.byte(str, i, j) == str:byte(i, j)) + local char_code = string.byte(str, i, j) + if char_code then + assert(type(char_code) == "number") + local byte = string.char(char_code) + assert(byte) + assert(byte == str) + end +end + +local args = { + artifact_prefix = "string_byte_", +} +luzer.Fuzz(TestOneInput, nil, args) diff --git a/tests/lapi/string_char_test.lua b/tests/lapi/string_char_test.lua new file mode 100644 index 0000000..919ac2a --- /dev/null +++ b/tests/lapi/string_char_test.lua @@ -0,0 +1,41 @@ +--[[ +SPDX-License-Identifier: ISC +Copyright (c) 2023-2025, Sergey Bronnikov. + +6.4 – String Manipulation +https://www.lua.org/manual/5.3/manual.html#6.4 + +string.char bug, +https://github.com/LuaJIT/LuaJIT/issues/375 + +Fix string.char() recording with no arguments, +https://github.com/LuaJIT/LuaJIT/commit/dfa692b7 + +Synopsis: string.char(...) +]] + +local luzer = require("luzer") +local test_lib = require("lib") + +local unpack = unpack or table.unpack + +local function TestOneInput(buf, _size) + local fdp = luzer.FuzzedDataProvider(buf) + os.setlocale(test_lib.random_locale(fdp), "all") + -- `n` must be less than UINT_MAX and there are at least extra + -- free stack slots in the stack, otherwise an error + -- "too many results to unpack" is raised, see . + local MAX_CHARS_NUM = 1024 + local n = fdp:consume_integer(1, MAX_CHARS_NUM) + local CHAR_MAX = 255 + local chs = fdp:consume_integers(0, CHAR_MAX, n) + local str = string.char(unpack(chs)) + -- Returns a string with length equal to the number of + -- arguments. + assert(#str == n) +end + +local args = { + artifact_prefix = "string_char_", +} +luzer.Fuzz(TestOneInput, nil, args) diff --git a/tests/lapi/string_dump_test.lua b/tests/lapi/string_dump_test.lua new file mode 100644 index 0000000..178012c --- /dev/null +++ b/tests/lapi/string_dump_test.lua @@ -0,0 +1,40 @@ +--[[ +SPDX-License-Identifier: ISC +Copyright (c) 2023-2025, Sergey Bronnikov. + +6.4 – String Manipulation +https://www.lua.org/manual/5.3/manual.html#6.4 + +string.dump(table.foreach) will trigger an assert, +https://github.com/LuaJIT/LuaJIT/issues/1038 + +An emergency collection when handling an error while loading the upvalues of a function can cause a segfault, +https://github.com/lua/lua/commit/422ce50d2e8856ed789d1359c673122dbb0088ea + +Synopsis: string.dump(function [, strip]) +]] + +local luzer = require("luzer") +local test_lib = require("lib") + +local function TestOneInput(buf, _size) + local fdp = luzer.FuzzedDataProvider(buf) + os.setlocale(test_lib.random_locale(fdp), "all") + local str = fdp:consume_string(test_lib.MAX_STR_LEN) + local strip = fdp:consume_boolean() + local ok, func = pcall(loadstring, str) + if not ok or func == nil then + return + end + local res = string.dump(func, strip) + assert(#res ~= 0) +end + +local args = { + artifact_prefix = "string_dump_", +} +-- LuaJIT ASSERT lj_bcread.c:123: bcread_byte: buffer read overflow. +if test_lib.lua_version() == "LuaJIT" then + args["only_ascii"] = 1 +end +luzer.Fuzz(TestOneInput, nil, args) diff --git a/tests/lapi/string_find_test.lua b/tests/lapi/string_find_test.lua new file mode 100644 index 0000000..a98864a --- /dev/null +++ b/tests/lapi/string_find_test.lua @@ -0,0 +1,48 @@ +--[=[[ +SPDX-License-Identifier: ISC +Copyright (c) 2023-2025, Sergey Bronnikov. + +6.4 – String Manipulation +https://www.lua.org/manual/5.3/manual.html#6.4 + +Bug in "Don't use STRREF for pointer diff in string.find().", +https://github.com/LuaJIT/LuaJIT/issues/540 + +Some patterns can overflow the C stack, due to recursion, +https://www.lua.org/bugs.html#5.2.1-1 + +Properly fix pointer diff in string.find(), +https://github.com/LuaJIT/LuaJIT/commit/0bee44c9 + +Synopsis: string.find(s, pattern [, init [, plain]]) +]]=] + +local luzer = require("luzer") +local test_lib = require("lib") + +local function TestOneInput(buf, _size) + local fdp = luzer.FuzzedDataProvider(buf) + os.setlocale(test_lib.random_locale(fdp), "all") + local str = fdp:consume_string(test_lib.MAX_STR_LEN) + local pattern = fdp:consume_string(test_lib.MAX_STR_LEN) + local init = fdp:consume_integer(0, test_lib.MAX_INT) + local plain = fdp:consume_boolean() + -- Avoid errors like "malformed pattern (missing ']')". + local ok, _ = pcall(string.find, str, pattern, init, plain) + if not ok then + return + end + local begin_pos, end_pos = string.find(str, pattern, init, plain) + -- `string.format()` returns two numbers or "fail". + assert((type(begin_pos) == "number" and type(end_pos) == "number") or + (begin_pos == nil or end_pos == nil) or + begin_pos == "fail") + -- `string.format()` and `string:format()` is the same. + assert(string.find(str, pattern, init, plain) == + str:find(pattern, init, plain)) +end + +local args = { + artifact_prefix = "string_find_", +} +luzer.Fuzz(TestOneInput, nil, args) diff --git a/tests/lapi/string_format_test.lua b/tests/lapi/string_format_test.lua new file mode 100644 index 0000000..e58da00 --- /dev/null +++ b/tests/lapi/string_format_test.lua @@ -0,0 +1,83 @@ +--[[ +SPDX-License-Identifier: ISC +Copyright (c) 2023-2025, Sergey Bronnikov. + +6.4 – String Manipulation +https://www.lua.org/manual/5.3/manual.html#6.4 + +stack-buffer-overflow in lj_strfmt_wfnum, +https://github.com/LuaJIT/LuaJIT/issues/1149 +string.format("%7g",0x1.144399609d407p+401) + +string.format %c bug, +https://github.com/LuaJIT/LuaJIT/issues/378 + +string.format doesn't take current locale decimal separator into account, +https://github.com/LuaJIT/LuaJIT/issues/673 + +string.format("%f") can cause a buffer overflow (only when +'lua_Number' is long double!), +https://www.lua.org/bugs.html#5.3.0-1 + +string.format may get buffer as an argument when there are missing +arguments and format string is too long, +https://www.lua.org/bugs.html#5.1.4-7 + +string.format("%") may read past the string, +https://www.lua.org/bugs.html#5.1.1-3 + +Option '%q' in string.formatE does not handle '\r' correctly, +https://www.lua.org/bugs.html#5.1-4 + +FFI: Support FFI numbers in string.format() and buf:putf(), +https://github.com/LuaJIT/LuaJIT/commit/1b7171c3 + +[0014] CRASH detected in lj_ir_kgc due to a fault at or +near 0x00007ff7f3274008 leading to SIGSEGV, +https://github.com/LuaJIT/LuaJIT/issues/1203 + +Synopsis: string.format(formatstring, ...) +]] + + +local luzer = require("luzer") +local test_lib = require("lib") + +local specifiers = { + "a", + "A", + "c", + "d", + "e", + "E", + "f", + "g", + "G", + "i", + "o", + "p", + "q", + "s", + "u", + "x", + "X", +} + +local function TestOneInput(buf, _size) + local fdp = luzer.FuzzedDataProvider(buf) + local spec = fdp:oneof(specifiers) + local format_string = ("%%%s"):format(spec) + local str = fdp:consume_string(test_lib.MAX_STR_LEN) + + os.setlocale(test_lib.random_locale(fdp), "all") + local ok, res = pcall(string.format, format_string, str) + assert(type(res) == "string") + if ok then + assert((format_string):format(str) == string.format(format_string, str)) + end +end + +local args = { + artifact_prefix = "string_format_", +} +luzer.Fuzz(TestOneInput, nil, args) diff --git a/tests/lapi/string_gmatch_test.lua b/tests/lapi/string_gmatch_test.lua new file mode 100644 index 0000000..8821279 --- /dev/null +++ b/tests/lapi/string_gmatch_test.lua @@ -0,0 +1,33 @@ +--[[ +SPDX-License-Identifier: ISC +Copyright (c) 2023-2025, Sergey Bronnikov. + +6.4 – String Manipulation +https://www.lua.org/manual/5.3/manual.html#6.4 + +GC64: string.gmatch crash, +https://github.com/LuaJIT/LuaJIT/issues/300 + +gmatch iterator fails when called from a coroutine different from +the one that created it, +https://www.lua.org/bugs.html#5.3.2-3 + +Synopsis: string.gmatch(s, pattern [, init]) +]] + +local luzer = require("luzer") +local test_lib = require("lib") + +local function TestOneInput(buf, _size) + local fdp = luzer.FuzzedDataProvider(buf) + os.setlocale(test_lib.random_locale(fdp), "all") + local s = fdp:consume_string(test_lib.MAX_STR_LEN) + local pattern = fdp:consume_string(test_lib.MAX_STR_LEN) + local init = fdp:consume_integer(0, test_lib.MAX_INT) + string.gmatch(s, pattern, init) +end + +local args = { + artifact_prefix = "string_gmatch_", +} +luzer.Fuzz(TestOneInput, nil, args) diff --git a/tests/lapi/string_gsub_test.lua b/tests/lapi/string_gsub_test.lua new file mode 100644 index 0000000..789d16d --- /dev/null +++ b/tests/lapi/string_gsub_test.lua @@ -0,0 +1,42 @@ +--[[ +SPDX-License-Identifier: ISC +Copyright (c) 2023-2025, Sergey Bronnikov. + +6.4 – String Manipulation +https://www.lua.org/manual/5.3/manual.html#6.4 + +Performance issue for reg expression with "$", +https://github.com/LuaJIT/LuaJIT/issues/118 + +LuaJIT's gsub does not work with zero bytes in the pattern string, +https://github.com/LuaJIT/LuaJIT/issues/860 + +gsub may go wild when wrongly called without its third argument +and with a large subject, +https://www.lua.org/bugs.html#5.1.2-9 + +Synopsis: string.gsub(s, pattern, repl [, n]) +]] + +local luzer = require("luzer") +local test_lib = require("lib") + +local function TestOneInput(buf, _size) + local fdp = luzer.FuzzedDataProvider(buf) + local str = fdp:consume_string(test_lib.MAX_STR_LEN) + local pattern = fdp:consume_string(test_lib.MAX_STR_LEN) + local repl = fdp:consume_string(test_lib.MAX_STR_LEN) + local n = fdp:consume_integer(0, test_lib.MAX_INT) + + os.setlocale(test_lib.random_locale(fdp), "all") + -- Avoid errors like "malformed pattern (missing ']')". + local ok, res = pcall(string.gsub, str, pattern, repl, n) + if ok then + assert(type(res) == "string") + end +end + +local args = { + artifact_prefix = "string_gsub_", +} +luzer.Fuzz(TestOneInput, nil, args) diff --git a/tests/lapi/string_len_test.lua b/tests/lapi/string_len_test.lua new file mode 100644 index 0000000..785c4c8 --- /dev/null +++ b/tests/lapi/string_len_test.lua @@ -0,0 +1,24 @@ +--[[ +SPDX-License-Identifier: ISC +Copyright (c) 2023-2025, Sergey Bronnikov. + +6.4 – String Manipulation +https://www.lua.org/manual/5.3/manual.html#6.4 + +Synopsis: string.len(s) +]] + +local luzer = require("luzer") +local test_lib = require("lib") + +local function TestOneInput(buf, _size) + local fdp = luzer.FuzzedDataProvider(buf) + os.setlocale(test_lib.random_locale(fdp), "all") + local str = fdp:consume_string(test_lib.MAX_STR_LEN) + assert(string.len(str) == #str) +end + +local args = { + artifact_prefix = "string_len_", +} +luzer.Fuzz(TestOneInput, nil, args) diff --git a/tests/lapi/string_lower_test.lua b/tests/lapi/string_lower_test.lua new file mode 100644 index 0000000..00da732 --- /dev/null +++ b/tests/lapi/string_lower_test.lua @@ -0,0 +1,32 @@ +--[[ +SPDX-License-Identifier: ISC +Copyright (c) 2023-2025, Sergey Bronnikov. + +6.4 – String Manipulation +https://www.lua.org/manual/5.3/manual.html#6.4 + +Synopsis: string.lower(s) +]] + +local luzer = require("luzer") +local test_lib = require("lib") + +local function TestOneInput(buf, _) + local fdp = luzer.FuzzedDataProvider(buf) + os.setlocale(test_lib.random_locale(fdp), "all") + local lower_bound = string.byte("A") + local upper_bound = string.byte("A") + 25 + local ch_code = fdp:consume_integer(lower_bound, upper_bound) + local ch_uppercase = string.char(ch_code) + local ch = string.upper(string.lower(ch_uppercase)) + assert(ch == ch_uppercase) + + local str = fdp:consume_string(1, test_lib.MAX_STR_LEN) + local str_lowercase = str:lower() + assert(str_lowercase == str_lowercase:upper():lower()) +end + +local args = { + artifact_prefix = "string_lower_", +} +luzer.Fuzz(TestOneInput, nil, args) diff --git a/tests/lapi/string_match_test.lua b/tests/lapi/string_match_test.lua new file mode 100644 index 0000000..76cf215 --- /dev/null +++ b/tests/lapi/string_match_test.lua @@ -0,0 +1,36 @@ +--[[ +SPDX-License-Identifier: ISC +Copyright (c) 2023-2025, Sergey Bronnikov. + +6.4 – String Manipulation +https://www.lua.org/manual/5.3/manual.html#6.4 + +Synopsis: string.match(s, pattern [, init]) +]] + +local luzer = require("luzer") +local test_lib = require("lib") + +local function TestOneInput(buf, _size) + local fdp = luzer.FuzzedDataProvider(buf) + os.setlocale(test_lib.random_locale(fdp), "all") + local str = fdp:consume_string(test_lib.MAX_STR_LEN) + local pattern = fdp:consume_string(test_lib.MAX_STR_LEN) + local init = fdp:consume_integer(0, test_lib.MAX_INT) + -- Avoid errors like "malformed pattern (ends with '%')". + local ok, res = pcall(string.match, str, pattern, init) + if not ok then + return + end + -- Lua 5.4 Reference manual says, that "if it finds one, then + -- match returns the captures from the pattern; otherwise it + -- returns fail. If pattern specifies no captures, then the + -- whole match is returned.". On practice with empty pattern + -- `nil` is returned. + assert(res == nil or type(res) == "string") +end + +local args = { + artifact_prefix = "string_match_", +} +luzer.Fuzz(TestOneInput, nil, args) diff --git a/tests/lapi/string_pack_test.lua b/tests/lapi/string_pack_test.lua new file mode 100644 index 0000000..0166d75 --- /dev/null +++ b/tests/lapi/string_pack_test.lua @@ -0,0 +1,35 @@ +--[[ +SPDX-License-Identifier: ISC +Copyright (c) 2023-2025, Sergey Bronnikov. + +6.4 – String Manipulation +https://www.lua.org/manual/5.3/manual.html#6.4 + +Synopsis: string.pack(fmt, v1, v2, ...) +]] + +local luzer = require("luzer") +local test_lib = require("lib") + +-- The function `string.pack()` is available since Lua 5.3. +if not test_lib.lua_current_version_ge_than(5, 3) then + print("Unsupported version.") + os.exit(0) +end + +local function TestOneInput(buf, _size) + local fdp = luzer.FuzzedDataProvider(buf) + os.setlocale(test_lib.random_locale(fdp), "all") + local fmt_str = fdp:consume_string(0, test_lib.MAX_INT) + if fdp:remaining_bytes() == 0 then + return -1 + end + local n = fdp:consume_integer(1, test_lib.MAX_INT) + local values = fdp:consume_strings(test_lib.MAX_STR_LEN, n) + string.pack(fmt_str, table.unpack(values)) +end + +local args = { + artifact_prefix = "string_pack_", +} +luzer.Fuzz(TestOneInput, nil, args) diff --git a/tests/lapi/string_packsize_test.lua b/tests/lapi/string_packsize_test.lua new file mode 100644 index 0000000..8bc3451 --- /dev/null +++ b/tests/lapi/string_packsize_test.lua @@ -0,0 +1,36 @@ +--[[ +SPDX-License-Identifier: ISC +Copyright (c) 2023-2025, Sergey Bronnikov. + +6.4 – String Manipulation +https://www.lua.org/manual/5.3/manual.html#6.4 + +Synopsis: string.packsize(fmt) +]] + +local luzer = require("luzer") +local test_lib = require("lib") + +-- The function `string.packsize()` is available since Lua 5.3. +if not test_lib.lua_current_version_ge_than(5, 3) then + print("Unsupported version.") + os.exit(0) +end + +local function TestOneInput(buf, _size) + local fdp = luzer.FuzzedDataProvider(buf) + os.setlocale(test_lib.random_locale(fdp), "all") + local fmt_str = fdp:consume_string(test_lib.MAX_STR_LEN) + -- Avoid errors like "invalid format option 'R'". + local ok, _ = pcall(string.packsize, fmt_str) + if not ok then + return + end + local size = string.packsize(fmt_str) + assert(type(size) == "number") +end + +local args = { + artifact_prefix = "string_packsize_", +} +luzer.Fuzz(TestOneInput, nil, args) diff --git a/tests/lapi/string_rep_test.lua b/tests/lapi/string_rep_test.lua new file mode 100644 index 0000000..08070e1 --- /dev/null +++ b/tests/lapi/string_rep_test.lua @@ -0,0 +1,31 @@ +--[[ +SPDX-License-Identifier: ISC +Copyright (c) 2023-2025, Sergey Bronnikov. + +6.4 – String Manipulation +https://www.lua.org/manual/5.3/manual.html#6.4 + +Synopsis: string.rep(s, n [, sep]) + +read overflow in 'l_strcmp', +https://github.com/lua/lua/commit/f623b969325be736297bc1dff48e763c08778243 +]] + +local luzer = require("luzer") +local test_lib = require("lib") + +local function TestOneInput(buf, _size) + local fdp = luzer.FuzzedDataProvider(buf) + os.setlocale(test_lib.random_locale(fdp), "all") + -- Huge length leads to slow units. + local n = fdp:consume_integer(0, test_lib.MAX_STR_LEN) + local s = fdp:consume_string(test_lib.MAX_STR_LEN) + local sep = fdp:consume_string(test_lib.MAX_STR_LEN) + local len = string.len(string.rep(s, n, sep)) + assert(len == #s * n + #sep * (n - 1)) +end + +local args = { + artifact_prefix = "string_rep_", +} +luzer.Fuzz(TestOneInput, nil, args) diff --git a/tests/lapi/string_reverse_test.lua b/tests/lapi/string_reverse_test.lua new file mode 100644 index 0000000..96cfe67 --- /dev/null +++ b/tests/lapi/string_reverse_test.lua @@ -0,0 +1,27 @@ +--[[ +SPDX-License-Identifier: ISC +Copyright (c) 2023-2025, Sergey Bronnikov. + +6.4 – String Manipulation +https://www.lua.org/manual/5.3/manual.html#6.4 + +Missing phi check in bufput_bufstr fold rule, +https://github.com/LuaJIT/LuaJIT/issues/797 + +Synopsis: string.reverse(s) +]] + +local luzer = require("luzer") +local test_lib = require("lib") + +local function TestOneInput(buf, _size) + local fdp = luzer.FuzzedDataProvider(buf) + os.setlocale(test_lib.random_locale(fdp), "all") + local str = fdp:consume_string(test_lib.MAX_STR_LEN) + assert(string.reverse(string.reverse(str)) == str) +end + +local args = { + artifact_prefix = "string_reverse_", +} +luzer.Fuzz(TestOneInput, nil, args) diff --git a/tests/lapi/string_sub_test.lua b/tests/lapi/string_sub_test.lua new file mode 100644 index 0000000..babea09 --- /dev/null +++ b/tests/lapi/string_sub_test.lua @@ -0,0 +1,28 @@ +--[[ +SPDX-License-Identifier: ISC +Copyright (c) 2023-2025, Sergey Bronnikov. + +6.4 – String Manipulation +https://www.lua.org/manual/5.3/manual.html#6.4 + +Synopsis: string.sub(s, i [, j]) +]] + +local luzer = require("luzer") +local test_lib = require("lib") + +local function TestOneInput(buf, _size) + local fdp = luzer.FuzzedDataProvider(buf) + os.setlocale(test_lib.random_locale(fdp), "all") + local str_len = test_lib.MAX_STR_LEN + local middle_str = fdp:consume_string(str_len) + local left_str = fdp:consume_string(str_len) + local right_str = fdp:consume_string(str_len) + local str = left_str .. middle_str .. right_str + assert(middle_str == string.sub(str, #left_str + 1, #left_str + #middle_str)) +end + +local args = { + artifact_prefix = "string_sub_", +} +luzer.Fuzz(TestOneInput, nil, args) diff --git a/tests/lapi/string_unpack_test.lua b/tests/lapi/string_unpack_test.lua new file mode 100644 index 0000000..aca0c47 --- /dev/null +++ b/tests/lapi/string_unpack_test.lua @@ -0,0 +1,44 @@ +--[[ +SPDX-License-Identifier: ISC +Copyright (c) 2023-2025, Sergey Bronnikov. + +6.4 – String Manipulation +https://www.lua.org/manual/5.3/manual.html#6.4 + +Synopsis: string.unpack(fmt, s [, pos]) +]] + +local luzer = require("luzer") +local test_lib = require("lib") + +-- The function `string.unpack()` is available since Lua 5.3. +if not test_lib.lua_current_version_ge_than(5, 3) then + print("Unsupported version.") + os.exit(0) +end + +local function TestOneInput(buf, _size) + local fdp = luzer.FuzzedDataProvider(buf) + os.setlocale(test_lib.random_locale(fdp), "all") + local str = fdp:consume_string(1, test_lib.MAX_STR_LEN) + local fmt_str = fdp:consume_string(1, test_lib.MAX_STR_LEN) + local pos = fdp:consume_integer(0, test_lib.MAX_INT) + + local ok, _ = pcall(string.unpack, fmt_str, str, pos) + if not ok then + return + end + local packed = string.pack(fmt_str, string.unpack(fmt_str, str, pos)) + if #packed == 0 then + return + end + assert(packed == str) + assert(#packed == string.packsize(fmt_str)) +end + +local args = { + -- Avoid errors like "invalid format option '�'" is expected". + only_ascii = 1, + artifact_prefix = "string_unpack_", +} +luzer.Fuzz(TestOneInput, nil, args) diff --git a/tests/lapi/string_upper_test.lua b/tests/lapi/string_upper_test.lua new file mode 100644 index 0000000..d5f9a43 --- /dev/null +++ b/tests/lapi/string_upper_test.lua @@ -0,0 +1,29 @@ +--[[ +SPDX-License-Identifier: ISC +Copyright (c) 2023-2025, Sergey Bronnikov. + +6.4 – String Manipulation +https://www.lua.org/manual/5.3/manual.html#6.4 + +Synopsis: string.upper(s) +]] + +local luzer = require("luzer") +local test_lib = require("lib") + +local function TestOneInput(buf, _size) + local fdp = luzer.FuzzedDataProvider(buf) + os.setlocale(test_lib.random_locale(fdp), "all") + local a_code = string.byte("a") + local str = fdp:consume_string(1, test_lib.MAX_STR_LEN) + local str_uppercase = str:upper() + for i = 1, #str do + local code = string.byte(string.sub(str_uppercase, i, i)) + assert(code < a_code or code > a_code + 25) + end +end + +local args = { + artifact_prefix = "string_upper_", +} +luzer.Fuzz(TestOneInput, nil, args)