Skip to content

tests/lapi: add builtin tests #139

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions tests/lapi/builtin_assert_test.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
--[[
SPDX-License-Identifier: ISC
Copyright (c) 2023-2025, Sergey Bronnikov.

5.1 – Basic Functions
https://www.lua.org/manual/5.1/manual.html

Synopsis: assert(v [, message])
]]

local luzer = require("luzer")
local test_lib = require("lib")
local MAX_INT = test_lib.MAX_INT

local function TestOneInput(buf)
local fdp = luzer.FuzzedDataProvider(buf)
local max_len = fdp:consume_integer(0, MAX_INT)
local message = fdp:consume_string(max_len)
local v = fdp:consume_boolean()
local ok, _ = pcall(assert, v, message)
assert(ok == v)
end

local args = {
artifact_prefix = "builtin_assert_",
}
luzer.Fuzz(TestOneInput, nil, args)
60 changes: 60 additions & 0 deletions tests/lapi/builtin_collectgarbage_test.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
--[=[[
SPDX-License-Identifier: ISC
Copyright (c) 2023-2025, Sergey Bronnikov.

5.1 – Basic Functions
https://www.lua.org/manual/5.1/manual.html

Synopsis: collectgarbage([opt [, arg]])
]]=]

local luzer = require("luzer")
local test_lib = require("lib")

local unpack = unpack or table.unpack

local gc_mode = {
"collect",
"count",
"restart",
"step",
"stop",
}

if test_lib.lua_version() == "LuaJIT" then
table.insert(gc_mode, "setpause")
table.insert(gc_mode, "setstepmul")
else
table.insert(gc_mode, "isrunning")
table.insert(gc_mode, "incremental")
table.insert(gc_mode, "generational")
end

local function TestOneInput(buf)
local fdp = luzer.FuzzedDataProvider(buf)
local mode = fdp:oneof(gc_mode)
local MAX_INT = test_lib.MAX_INT
local arg = {}
if mode == "step" or
mode == "setpause" or
mode == "setstepmul" then
table.insert(arg, fdp:consume_integer(0, MAX_INT))
end
-- This option can be followed by two numbers: the
-- garbage-collector minor multiplier and the major multiplier.
if mode == "generational" then
table.insert(arg, fdp:consume_integer(0, MAX_INT))
end
-- This option can be followed by three numbers: the
-- garbage-collector pause, the step multiplier, and the step
-- size
if mode == "incremental" then
table.insert(arg, fdp:consume_integer(0, MAX_INT))
end
collectgarbage(mode, unpack(arg))
end

local args = {
artifact_prefix = "builtin_collectgarbage_",
}
luzer.Fuzz(TestOneInput, nil, args)
55 changes: 55 additions & 0 deletions tests/lapi/builtin_concat_test.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
--[[
SPDX-License-Identifier: ISC
Copyright (c) 2023-2025, Sergey Bronnikov.

2.5.4 – Concatenation
https://www.lua.org/manual/5.1/manual.html

Recording of __concat in GC64 mode,
https://github.com/LuaJIT/LuaJIT/issues/839

Bug: Unbalanced Stack After Hot Instruction error on table concatenation,
https://github.com/LuaJIT/LuaJIT/issues/690

LJ_GC64: Fix lua_concat(),
https://github.com/LuaJIT/LuaJIT/issues/881

Buffer overflow in string concatenation,
https://github.com/lua/lua/commit/5853c37a83ec66ccb45094f9aeac23dfdbcde671

Wrong assert when reporting concatenation errors
(manifests only when Lua is compiled in debug mode).
https://www.lua.org/bugs.html#5.2.2-3

Wrong error message in some concatenations,
https://www.lua.org/bugs.html#5.1.2-5

Concat metamethod converts numbers to strings,
https://www.lua.org/bugs.html#5.1.1-8

String concatenation may cause arithmetic overflow, leading to
a buffer overflow,
https://www.lua.org/bugs.html#5.0.2-1
]]

local luzer = require("luzer")
local test_lib = require("lib")

local function TestOneInput(buf)
local fdp = luzer.FuzzedDataProvider(buf)
local str = fdp:consume_string(test_lib.MAX_STR_LEN)
if str == nil then return -1 end
local str_chars = {}
str:gsub(".", function(c) table.insert(str_chars, c) end)

local str_concat = ""
for _, c in ipairs(str_chars) do
str_concat = str_concat .. c
end
assert(str_concat == str)
end

local args = {
artifact_prefix = "builtin_concat_",
}
luzer.Fuzz(TestOneInput, nil, args)
30 changes: 30 additions & 0 deletions tests/lapi/builtin_dofile_test.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
--[[
SPDX-License-Identifier: ISC
Copyright (c) 2023-2025, Sergey Bronnikov.

5.1 – Basic Functions
https://www.lua.org/manual/5.1/manual.html

Synopsis: dofile([filename])
]]

local luzer = require("luzer")
local test_lib = require("lib")

local function TestOneInput(buf)
local chunk_filename = os.tmpname()
local fh = io.open(chunk_filename, "w")
fh:write(buf)
fh:close()
pcall(dofile, chunk_filename)
os.remove(chunk_filename)
end

local args = {
artifact_prefix = "builtin_dofile_",
}
-- 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)
27 changes: 27 additions & 0 deletions tests/lapi/builtin_dostring_test.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
--[[
SPDX-License-Identifier: ISC
Copyright (c) 2023-2025, Sergey Bronnikov.

5.1 – Basic Functions
https://www.lua.org/manual/5.1/manual.html
]]

local luzer = require("luzer")
local test_lib = require("lib")
local MAX_INT = test_lib.MAX_INT

local function TestOneInput(buf)
local fdp = luzer.FuzzedDataProvider(buf)
local max_len = fdp:consume_integer(0, MAX_INT)
local str = fdp:consume_string(max_len)
pcall(loadstring, str)
end

local args = {
artifact_prefix = "builtin_dostring_",
}
-- 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)
33 changes: 33 additions & 0 deletions tests/lapi/builtin_error_test.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
--[[
SPDX-License-Identifier: ISC
Copyright (c) 2023-2025, Sergey Bronnikov.

5.1 – Basic Functions
https://www.lua.org/manual/5.1/manual.html

Synopsis: error(message [, level])
]]

local luzer = require("luzer")
local test_lib = require("lib")
local MAX_INT = test_lib.MAX_INT

local function escape_pattern(text)
return (text:gsub("[-.+%[%]()$^%%?*]", "%%%1"))
end

local function TestOneInput(buf)
local fdp = luzer.FuzzedDataProvider(buf)
local level = fdp:consume_integer(0, MAX_INT)
local message_len = fdp:consume_integer(0, MAX_INT)
local message = fdp:consume_string(message_len)
local ok, err = pcall(error, message, level)
assert(ok == false)
-- Escape message to avoid error "invalid pattern capture".
assert(err:match(escape_pattern(message)) == message)
end

local args = {
artifact_prefix = "builtin_error_",
}
luzer.Fuzz(TestOneInput, nil, args)
46 changes: 46 additions & 0 deletions tests/lapi/builtin_getfenv_test.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
--[[
SPDX-License-Identifier: ISC
Copyright (c) 2023-2025, Sergey Bronnikov.

5.1 – Basic Functions
https://www.lua.org/manual/5.1/manual.html

debug.getfenv does not check whether it has an argument,
https://www.lua.org/bugs.html#5.1.4-5

module may change the environment of a C function,
https://www.lua.org/bugs.html#5.1.3-11

UBSan warning for too big/small getfenv/setfenv level,
https://github.com/LuaJIT/LuaJIT/issues/1329

Synopsis: getfenv([f])
]]

local luzer = require("luzer")
local test_lib = require("lib")

-- Lua 5.2: Functions setfenv and getfenv were removed, because
-- of the changes in environments.
if test_lib.lua_current_version_ge_than(5, 2) then
print("Unsupported version.")
os.exit(0)
end

local function TestOneInput(buf)
local fdp = luzer.FuzzedDataProvider(buf)
local level = fdp:consume_integer(0, test_lib.MAX_INT)
local fenv, err = pcall(getfenv, level)
if err then
return -1
end
local magic_str = fdp:consume_string(test_lib.MAX_STR_LEN)
fenv["magic"] = magic_str
setfenv(level, fenv)
assert(getfenv(level).magic == magic_str)
end

local args = {
artifact_prefix = "builtin_getfenv_",
}
luzer.Fuzz(TestOneInput, nil, args)
33 changes: 33 additions & 0 deletions tests/lapi/builtin_getmetatable_test.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
--[[
SPDX-License-Identifier: ISC
Copyright (c) 2023-2025, Sergey Bronnikov.

5.1 – Basic Functions
https://www.lua.org/manual/5.1/manual.html

Incorrect recording of getmetatable() for IO handlers,
https://github.com/LuaJIT/LuaJIT/issues/1279

Synopsis: getmetatable(object)
]]

local luzer = require("luzer")
local test_lib = require("lib")

local function TestOneInput(buf)
local tbl = {}
local fdp = luzer.FuzzedDataProvider(buf)
-- Build a random table and set a known key to it to check
-- it's presence in metatable.
local MAX_N = 100
local count = fdp:consume_integer(0, MAX_N)
local mt = fdp:consume_integers(test_lib.MIN_INT, test_lib.MAX_INT, count)
setmetatable(tbl, mt)
local metatable = getmetatable(tbl)
assert(test_lib.arrays_equal(metatable, mt))
end

local args = {
artifact_prefix = "builtin_getmetatable_",
}
luzer.Fuzz(TestOneInput, nil, args)
28 changes: 28 additions & 0 deletions tests/lapi/builtin_ipairs_test.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
--[[
SPDX-License-Identifier: ISC
Copyright (c) 2023-2025, Sergey Bronnikov.

5.1 – Basic Functions
https://www.lua.org/manual/5.1/manual.html

Synopsis: ipairs(t)
]]

local luzer = require("luzer")
local test_lib = require("lib")

local function TestOneInput(buf)
local fdp = luzer.FuzzedDataProvider(buf)
local MAX_N = 1000
local count = fdp:consume_integer(0, MAX_N)
local tbl = fdp:consume_integers(test_lib.MIN_INT, test_lib.MAX_INT, count)
for i, v in ipairs(tbl) do
assert(type(i) == "number")
assert(type(v) == "number")
end
end

local args = {
artifact_prefix = "builtin_ipairs_",
}
luzer.Fuzz(TestOneInput, nil, args)
34 changes: 34 additions & 0 deletions tests/lapi/builtin_length_test.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
--[[
SPDX-License-Identifier: ISC
Copyright (c) 2023-2025, Sergey Bronnikov.

2.5.5 – The Length Operator
https://www.lua.org/manual/5.1/manual.html

Table length computation overflows for sequences larger than
2^31 elements, https://www.lua.org/bugs.html#5.3.4-3
]]

local luzer = require("luzer")
local test_lib = require("lib")

local unpack = unpack or table.unpack

local function TestOneInput(buf)
local fdp = luzer.FuzzedDataProvider(buf)

local str = fdp:consume_string(test_lib.MAX_STR_LEN)
if str == nil then return -1 end
local str_chars = {}
str:gsub(".", function(c) table.insert(str_chars, c) end)

assert(#str == select("#", unpack(str_chars)))
assert(#str == string.len(str))
assert(#str == str:len())
assert(#str_chars == str:len())
end

local args = {
artifact_prefix = "builtin_length_",
}
luzer.Fuzz(TestOneInput, nil, args)
Loading