Skip to content

Commit fb812c7

Browse files
committed
tests/lapi: add debug tests
The commit adds tests for the following functions in debug library: `debug.getlocal()`, `debug.getupvalue()` and `debug.getinfo()`. TODO: debug.getmetatable debug.setmetatable debug.setupvalue debug.setlocal
1 parent 8ef72ef commit fb812c7

File tree

3 files changed

+224
-0
lines changed

3 files changed

+224
-0
lines changed

tests/lapi/debug_getinfo_test.lua

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
--[[
2+
SPDX-License-Identifier: ISC
3+
Copyright (c) 2023-2025, Sergey Bronnikov.
4+
5+
Parameter 'what' of 'debug.getinfo' cannot start with '>',
6+
https://www.lua.org/bugs.html#5.4.2-2
7+
8+
Access to debug information in line hook of stripped function,
9+
https://github.com/lua/lua/commit/ae5b5ba529753c7a653901ffc29b5ea24c3fdf3a
10+
11+
Return hook may not see correct values for active local variables when function returns,
12+
https://www.lua.org/bugs.html#5.3.0-4
13+
14+
The PC out-of-range in lj_debug_frameline(),
15+
https://github.com/LuaJIT/LuaJIT/issues/1369
16+
17+
LuaJIT segfault in debug.getinfo(),
18+
https://github.com/LuaJIT/LuaJIT/issues/509
19+
20+
Synopsis: debug.getinfo([thread,] function [, what])
21+
]]
22+
23+
local luzer = require("luzer")
24+
local test_lib = require("lib")
25+
26+
local what = {
27+
"n", -- fills in the field name and namewhat.
28+
"S", -- fills in the fields source, short_src, linedefined,
29+
-- lastlinedefined, and what.
30+
"l", -- fills in the field currentline.
31+
"u", -- fills in the field nups.
32+
"f", -- pushes onto the stack the function that is running at
33+
-- the given level.
34+
"L", -- pushes onto the stack a table whose indices are the
35+
-- numbers of the lines that are valid on the function.
36+
}
37+
38+
local what_modes
39+
40+
local hook_mask = {
41+
"c", -- the hook is called every time Lua calls a function.
42+
"r", -- the hook is called every time Lua returns from a
43+
-- function.
44+
"l", -- the hook is called every time Lua enters a new line of
45+
-- code.
46+
}
47+
48+
local loadstring = type(loadstring) == "function" and loadstring or load
49+
50+
local function debug_hook()
51+
debug.getinfo(1, table.concat(what_modes))
52+
end
53+
54+
local function TestOneInput(buf)
55+
local fdp = luzer.FuzzedDataProvider(buf)
56+
57+
-- Generate a random 'what'.
58+
what_modes = {}
59+
local n_modes = fdp:consume_integer(0, #what)
60+
for _ = 0, n_modes do
61+
table.insert(what_modes, fdp:oneof(what))
62+
end
63+
64+
-- Generate a random hook mask.
65+
local n_hook_mask = fdp:consume_integer(0, #hook_mask)
66+
local mask = {}
67+
for _ = 0, n_hook_mask do
68+
table.insert(mask, fdp:oneof(hook_mask))
69+
end
70+
71+
-- Turn on the hook.
72+
debug.sethook(debug_hook, table.concat(mask))
73+
74+
local code = fdp:consume_string(test_lib.MAX_STR_LEN)
75+
local chunk = loadstring(code)
76+
if chunk == nil then
77+
return -1
78+
end
79+
pcall(chunk)
80+
81+
-- Turn off the hook.
82+
debug.sethook()
83+
end
84+
85+
local args = {
86+
artifact_prefix = "debug_getinfo_",
87+
}
88+
-- lj_bcread.c:123: bcread_byte: buffer read overflow
89+
if test_lib.lua_version() == "LuaJIT" then
90+
args.only_ascii = 1
91+
end
92+
luzer.Fuzz(TestOneInput, nil, args)

tests/lapi/debug_getlocal_test.lua

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
--[[
2+
SPDX-License-Identifier: ISC
3+
Copyright (c) 2023-2025, Sergey Bronnikov.
4+
5+
Negation overflow in getlocal/setlocal,
6+
https://github.com/lua/lua/commit/a585eae6e7ada1ca9271607a4f48dfb17868ab7b
7+
8+
debug.getlocal on a coroutine suspended in a hook can crash the interpreter,
9+
https://www.lua.org/bugs.html#5.3.0-2
10+
11+
Synopsis: debug.getlocal([thread,] level, local)
12+
]]
13+
14+
local luzer = require("luzer")
15+
local test_lib = require("lib")
16+
17+
-- Extract locals.
18+
local function debug_hook()
19+
local level = 2
20+
local i = 1
21+
while true do
22+
local name, _ = debug.getlocal(level, i)
23+
if name == nil then break end
24+
i = i + 1
25+
end
26+
end
27+
28+
local loadstring = type(loadstring) == "function" and loadstring or load
29+
30+
local hook_mask = {
31+
"c", -- the hook is called every time Lua calls a function.
32+
"r", -- the hook is called every time Lua returns from a function.
33+
"l", -- the hook is called every time Lua enters a new line of code.
34+
}
35+
36+
local function TestOneInput(buf)
37+
local fdp = luzer.FuzzedDataProvider(buf)
38+
39+
-- Generate a random hook mask.
40+
local n_hook_mask = fdp:consume_integer(0, #hook_mask)
41+
local mask = {}
42+
for _ = 0, n_hook_mask do
43+
table.insert(mask, fdp:oneof(hook_mask))
44+
end
45+
46+
-- Turn on the hook.
47+
debug.sethook(debug_hook, table.concat(mask))
48+
49+
local code = fdp:consume_string(test_lib.MAX_STR_LEN)
50+
local chunk = loadstring(code)
51+
if chunk == nil then
52+
return -1
53+
end
54+
pcall(chunk)
55+
56+
-- Turn off the hook.
57+
debug.sethook()
58+
end
59+
60+
local args = {
61+
artifact_prefix = "debug_getlocal_",
62+
}
63+
-- lj_bcread.c:123: bcread_byte: buffer read overflow
64+
if test_lib.lua_version() == "LuaJIT" then
65+
args.only_ascii = 1
66+
end
67+
luzer.Fuzz(TestOneInput, nil, args)

tests/lapi/debug_getupvalue_test.lua

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
--[[
2+
SPDX-License-Identifier: ISC
3+
Copyright (c) 2023-2025, Sergey Bronnikov.
4+
5+
lua_getupvalue and lua_setupvalue do not check for index too small,
6+
https://www.lua.org/bugs.html#5.0.2-2
7+
8+
Synopsis: debug.getupvalue (f, up)
9+
]]
10+
11+
local luzer = require("luzer")
12+
local test_lib = require("lib")
13+
14+
-- Extract upvalues.
15+
local function debug_hook()
16+
local level = 2
17+
local ar = debug.getinfo(level)
18+
local func = ar.func
19+
local nups = debug.getinfo(level, 'u').nups
20+
for i = 1, nups do
21+
local n, _ = debug.getupvalue(func, i)
22+
if not n then break end
23+
end
24+
end
25+
26+
local loadstring = type(loadstring) == "function" and loadstring or load
27+
28+
local hook_mask = {
29+
"c", -- the hook is called every time Lua calls a function.
30+
"r", -- the hook is called every time Lua returns from a function.
31+
"l", -- the hook is called every time Lua enters a new line of code.
32+
}
33+
34+
local function TestOneInput(buf)
35+
local fdp = luzer.FuzzedDataProvider(buf)
36+
37+
-- Generate a random hook mask.
38+
local n_hook_mask = fdp:consume_integer(0, #hook_mask)
39+
local mask = {}
40+
for _ = 0, n_hook_mask do
41+
table.insert(mask, fdp:oneof(hook_mask))
42+
end
43+
44+
-- Turn on the hook.
45+
debug.sethook(debug_hook, table.concat(mask))
46+
47+
local code = fdp:consume_string(test_lib.MAX_STR_LEN)
48+
local chunk = loadstring(code)
49+
if chunk == nil then
50+
return -1
51+
end
52+
pcall(chunk)
53+
54+
-- Turn off the hook.
55+
debug.sethook()
56+
end
57+
58+
local args = {
59+
artifact_prefix = "debug_getupvalue_",
60+
}
61+
-- lj_bcread.c:123: bcread_byte: buffer read overflow
62+
if test_lib.lua_version() == "LuaJIT" then
63+
args.only_ascii = 1
64+
end
65+
luzer.Fuzz(TestOneInput, nil, args)

0 commit comments

Comments
 (0)