Skip to content

Commit ebe7adb

Browse files
committed
tests/lapi: add bitop tests
The patch adds initial infrastructure for Lua API tests and changes existed infrastructure for running these tests. The patch reqires commit "cmake: allow to set a Lua library outside" [5] in the `luzer` project. The patch adds a fuzzing tests for Lua bitwise operations. In LuaJIT, these operations are implemented as a Lua functions in BitOp module [1], that later became a part of LuaJIT. In PUC Rio Lua, bitwise operations were added since version 5.2 [2] in "bit32" library. In Lua 5.3 the bit32 library has been deprecated and its operations were replaced by bitwise operations. The proposed bitop tests is capable to reproduce a LuaJIT issue with bit op coercion for shifts in DUALNUM builds [6]. 1. http://bitop.luajit.org/ 2. https://www.lua.org/manual/5.2/manual.html#6.7 3. https://www.lua.org/manual/5.3/manual.html#3.4.2 4. https://www.lua.org/manual/5.3/manual.html#8.2 5. ligurio/luzer@4ce52a6 6. LuaJIT/LuaJIT@69bbf3c1
1 parent 9be0f3b commit ebe7adb

17 files changed

+551
-3
lines changed

cmake/BuildLua.cmake

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ macro(build_lua LUA_VERSION)
55
set(LUA_PATCH_PATH ${PROJECT_SOURCE_DIR}/patches/puc-rio-lua.patch)
66

77
set(CFLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer")
8+
set(CFLAGS "${CMAKE_C_FLAGS} -DLUA_USE_DLOPEN")
89
if (ENABLE_LUA_ASSERT)
910
set(CFLAGS "${CFLAGS} -DLUAI_ASSERT")
1011
endif (ENABLE_LUA_ASSERT)
@@ -56,6 +57,9 @@ macro(build_lua LUA_VERSION)
5657
set(LDFLAGS "${LDFLAGS} -fprofile-instr-generate -fprofile-arcs -fcoverage-mapping -ftest-coverage")
5758
endif (ENABLE_COV)
5859

60+
# "relocation R_X86_64_PC32 against symbol `lua_isnumber' can
61+
# not be used when making a shared object; recompile with -fPIC".
62+
set(CFLAGS "${CFLAGS} -fPIC")
5963

6064
include(ExternalProject)
6165

cmake/BuildLuaJIT.cmake

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ macro(build_luajit LJ_VERSION)
8080
set(LDFLAGS "${LDFLAGS} -fprofile-instr-generate -fprofile-arcs -fcoverage-mapping -ftest-coverage")
8181
endif (ENABLE_COV)
8282

83+
# "relocation R_X86_64_PC32 against symbol `lua_isnumber' can
84+
# not be used when making a shared object; recompile with -fPIC".
85+
set(CFLAGS "${CFLAGS} -fPIC")
8386

8487
include(ExternalProject)
8588

tests/CMakeLists.txt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,2 @@
11
add_subdirectory(capi)
2-
if (ENABLE_BONUS_TESTS)
3-
add_subdirectory(lapi)
4-
endif()
2+
add_subdirectory(lapi)

tests/lapi/CMakeLists.txt

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
include(BuildLuzer)
2+
include(MakeLuaPath)
3+
4+
if(NOT LUA_EXECUTABLE)
5+
message(FATAL_ERROR "${LUA_EXECUTABLE} is not found.")
6+
endif()
7+
8+
make_lua_path(LUA_CPATH
9+
PATHS
10+
${LUZER_LUA_CPATH}
11+
)
12+
13+
make_lua_path(LUA_PATH
14+
PATHS
15+
${LUZER_LUA_PATH}
16+
${CMAKE_CURRENT_SOURCE_DIR}/?.lua
17+
)
18+
19+
set(DEFAULT_RUNS_NUMBER 5)
20+
21+
string(JOIN " " LIBFUZZER_OPTS
22+
-print_final_stats=1
23+
-print_pcs=1
24+
-reduce_inputs=1
25+
-reload=1
26+
-report_slow_units=5
27+
-runs=$\{RUNS:-${DEFAULT_RUNS_NUMBER}\}
28+
)
29+
30+
function(create_test)
31+
cmake_parse_arguments(
32+
FUZZ
33+
""
34+
"FILENAME"
35+
""
36+
${ARGN}
37+
)
38+
get_filename_component(test_name ${FUZZ_FILENAME} NAME_WE)
39+
string(REPLACE "_test" "" test_prefix ${test_name})
40+
set(dict_path ${PROJECT_SOURCE_DIR}/corpus/${test_prefix}.dict)
41+
set(corpus_path ${PROJECT_SOURCE_DIR}/corpus/${test_prefix})
42+
if (EXISTS ${dict_path})
43+
set(LIBFUZZER_OPTS "${LIBFUZZER_OPTS} -dict=${dict_path}")
44+
endif ()
45+
if (EXISTS ${corpus_path})
46+
set(LIBFUZZER_OPTS "${LIBFUZZER_OPTS} ${corpus_path}")
47+
endif ()
48+
add_test(NAME ${test_name}
49+
COMMAND ${SHELL} -c "${LUA_EXECUTABLE} ${FUZZ_FILENAME} ${LIBFUZZER_OPTS}"
50+
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
51+
)
52+
set_tests_properties(${test_name} PROPERTIES
53+
LABELS "lapi"
54+
ENVIRONMENT "LUA_PATH=${LUA_PATH};LUA_CPATH=${LUA_CPATH};ASAN_OPTIONS=detect_odr_violation=0"
55+
DEPENDS ${LUA_EXECUTABLE} ${LUZER_LIBRARY}
56+
)
57+
endfunction()
58+
59+
message(STATUS "Add Lua API test suite")
60+
file(GLOB tests LIST_DIRECTORIES false ${CMAKE_CURRENT_SOURCE_DIR} *_test.lua)
61+
foreach(filename ${tests})
62+
create_test(FILENAME ${filename})
63+
endforeach()

tests/lapi/bitop_arshift_test.lua

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--[[
2+
SPDX-License-Identifier: ISC
3+
Copyright (c) 2023-2025, Sergey Bronnikov.
4+
5+
Synopsis: bit32.arshift (x, disp)
6+
]]
7+
8+
local luzer = require("luzer")
9+
local test_lib = require("lib")
10+
11+
if test_lib.version() ~= "LuaJIT" then
12+
os.exit(0)
13+
end
14+
15+
local bitop = require("bit")
16+
local arshift = bitop.arshift
17+
18+
local function TestOneInput(buf)
19+
local fdp = luzer.FuzzedDataProvider(buf)
20+
local x = test_lib.random_number(fdp)
21+
local y = test_lib.random_number(fdp)
22+
arshift(x, y)
23+
end
24+
25+
local args = {
26+
artifact_prefix = "bitop_arshift_",
27+
}
28+
luzer.Fuzz(TestOneInput, nil, args)

tests/lapi/bitop_band_test.lua

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
--[[
2+
SPDX-License-Identifier: ISC
3+
Copyright (c) 2023-2025, Sergey Bronnikov.
4+
5+
Wrong code generation for constants in bitwise operations,
6+
https://github.com/lua/lua/commit/c764ca71a639f5585b5f466bea25dc42b855a4b0
7+
8+
Inconsistent behaviour of bit ops in DUALNUM mode,
9+
https://github.com/LuaJIT/LuaJIT/issues/1273
10+
11+
Synopsis: bit32.band (...)
12+
]]
13+
14+
local luzer = require("luzer")
15+
local test_lib = require("lib")
16+
17+
local band
18+
if test_lib.version() == "LuaJIT" then
19+
local bitop = require("bit")
20+
band = bitop.band
21+
else
22+
band = test_lib.bitwise_op("&")
23+
end
24+
25+
local function TestOneInput(buf)
26+
local fdp = luzer.FuzzedDataProvider(buf)
27+
local x = test_lib.random_number(fdp)
28+
local y = test_lib.random_number(fdp)
29+
local z = test_lib.random_number(fdp)
30+
31+
assert(band(x, band(y, z)) ==
32+
band(band(x, y), z), "x & (y & z) ~= (x & y) & z")
33+
assert(band(x, 0) == 0, "x & 0 ~= 0, where x = " .. x)
34+
35+
-- Broken?
36+
-- assert(band(x, 0xffff) == x, "x & 0xFFFF ~= x")
37+
-- assert(band(x, y) == band(y, z), "x & y ~= y & x")
38+
-- assert(band(x, x) == x, "x & x ~= x, where x = " .. x)
39+
end
40+
41+
local args = {
42+
artifact_prefix = "bitop_band_",
43+
}
44+
luzer.Fuzz(TestOneInput, nil, args)

tests/lapi/bitop_bnot_test.lua

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
--[[
2+
SPDX-License-Identifier: ISC
3+
Copyright (c) 2023-2025, Sergey Bronnikov.
4+
5+
Synopsis: bit32.bnot (x)
6+
]]
7+
8+
local luzer = require("luzer")
9+
local test_lib = require("lib")
10+
11+
local bnot
12+
if test_lib.version() == "LuaJIT" then
13+
local bitop = require("bit")
14+
bnot = bitop.bnot
15+
else
16+
bnot = test_lib.bitwise_op("~")
17+
end
18+
19+
local function TestOneInput(buf)
20+
local fdp = luzer.FuzzedDataProvider(buf)
21+
local x = test_lib.random_number(fdp)
22+
bnot(x)
23+
24+
-- For any integer x, the following identity holds [1]:
25+
-- 1. https://www.lua.org/manual/5.2/manual.html
26+
-- assert(bnot(x) == (-1 - x) % 2^32)
27+
28+
-- FIXME
29+
-- assert(bnot(bnot(x)) == x, "~(~x) ~= x")
30+
end
31+
32+
local args = {
33+
artifact_prefix = "bitop_bnot_",
34+
}
35+
luzer.Fuzz(TestOneInput, nil, args)

tests/lapi/bitop_bor_test.lua

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
--[[
2+
SPDX-License-Identifier: ISC
3+
Copyright (c) 2023-2025, Sergey Bronnikov.
4+
5+
ARM64: Should not fuse sign-extension into logical operands,
6+
can fuse rotations though,
7+
https://github.com/LuaJIT/LuaJIT/issues/1076
8+
9+
Wrong code generation for constants in bitwise operations,
10+
https://github.com/lua/lua/commit/c764ca71a639f5585b5f466bea25dc42b855a4b0
11+
12+
Synopsis: bit32.bor (···)
13+
]]
14+
15+
local luzer = require("luzer")
16+
local test_lib = require("lib")
17+
18+
local bor
19+
if test_lib.version() == "LuaJIT" then
20+
local bitop = require("bit")
21+
bor = bitop.bor
22+
else
23+
bor = test_lib.bitwise_op("|")
24+
end
25+
26+
local function TestOneInput(buf)
27+
local fdp = luzer.FuzzedDataProvider(buf)
28+
local x = test_lib.random_number(fdp)
29+
local y = test_lib.random_number(fdp)
30+
local z = test_lib.random_number(fdp)
31+
bor(x, y)
32+
33+
assert(bor(x, y) == bor(y, x), "x | y ~= y | x")
34+
assert(bor(x, bor(y, z)) == bor(bor(x, y), z),
35+
"x | (y | z) ~= (x | y) | z")
36+
37+
-- FIXME
38+
-- assert(bor(x, 0) == x, "x | 0 ~= x")
39+
-- assert(bor(x, 0xffff) == 0xffff, "x | 0xFFFF ~= 0xFFFF")
40+
-- assert(bor(x, x) == x, "x | x ~= x")
41+
end
42+
43+
local args = {
44+
artifact_prefix = "bitop_bor_",
45+
}
46+
luzer.Fuzz(TestOneInput, nil, args)

tests/lapi/bitop_bswap_test.lua

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--[[
2+
SPDX-License-Identifier: ISC
3+
Copyright (c) 2023-2025, Sergey Bronnikov.
4+
]]
5+
6+
local luzer = require("luzer")
7+
local test_lib = require("lib")
8+
9+
if test_lib.version() ~= "LuaJIT" then
10+
os.exit(0)
11+
end
12+
13+
local bitop = require("bit")
14+
local bswap = bitop.bswap
15+
16+
local function TestOneInput(buf)
17+
local fdp = luzer.FuzzedDataProvider(buf)
18+
local x = test_lib.random_number(fdp)
19+
local y = test_lib.random_number(fdp)
20+
bswap(x, y)
21+
end
22+
23+
local args = {
24+
artifact_prefix = "bitop_bswap_",
25+
}
26+
luzer.Fuzz(TestOneInput, nil, args)

tests/lapi/bitop_bxor_test.lua

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
--[[
2+
SPDX-License-Identifier: ISC
3+
Copyright (c) 2023-2025, Sergey Bronnikov.
4+
5+
Synopsis: bit32.bxor (...)
6+
]]
7+
8+
local luzer = require("luzer")
9+
local test_lib = require("lib")
10+
11+
local bxor
12+
local band
13+
local bor
14+
local bnot
15+
if test_lib.version() == "LuaJIT" then
16+
local bitop = require("bit")
17+
bxor = bitop.bxor
18+
band = bitop.band
19+
bor = bitop.bor
20+
bnot = bitop.bnot
21+
else
22+
bxor = test_lib.bitwise_op("~")
23+
band = test_lib.bitwise_op("&")
24+
bor = test_lib.bitwise_op("|")
25+
bnot = test_lib.bitwise_op("~")
26+
end
27+
28+
local function TestOneInput(buf)
29+
local fdp = luzer.FuzzedDataProvider(buf)
30+
local x = test_lib.random_number(fdp)
31+
local y = test_lib.random_number(fdp)
32+
local z = test_lib.random_number(fdp)
33+
34+
assert(bxor(x, y) == bxor(y, x), "x ^ y ~= y ^ x")
35+
assert(bxor(x, bxor(y, z)) == bxor(bxor(x, y), z),
36+
"x ^ (y ^ z) ~= (x ^ y) ^ z")
37+
assert(bxor(x, x) == 0, "x ^ x ~= 0")
38+
assert(bxor(x, y) == bor(band(x, bnot(y)), band(bnot(x), y)),
39+
"a ^ b = (a & ~b) | (~a & b)")
40+
41+
-- FIXME
42+
-- assert(bxor(x, 0xffff) == bnot(x), "x ^ 0xFFFF ~= ~x")
43+
-- assert(bxor(x, 0) == 0, "x ^ 0 ~= x")
44+
-- assert(bxor(bxor(x, y), y) == x, "x ^ y ^ y ~= x")
45+
-- assert(bxor(x, y) == band(bor(x, y), bor(bnot(x), bnot(y)),
46+
-- "a ^ b = (a | b) & (~a | ~b)"))
47+
end
48+
49+
local args = {
50+
artifact_prefix = "bitop_bxor_",
51+
}
52+
luzer.Fuzz(TestOneInput, nil, args)

0 commit comments

Comments
 (0)