commit 45231c6008e40c95016e9f7f8abef6b45b006b1f from: Sergey Bronnikov date: Fri Aug 18 16:44:37 2023 UTC mutator: get rid of custom libFuzzer mutator libFuzzer custom mutator for Lua has been moved to lua-c-api-tests [1]. 1. https://github.com/ligurio/lua-c-api-tests commit - 91af5ba5deda63dfb9987465ef2d2522fd2cacc8 commit + 45231c6008e40c95016e9f7f8abef6b45b006b1f blob - cd2d8a469ba69377072affd63bf2521f9d02713d blob + c40732f7974de9c042f41a65346e82dc73766783 --- CHANGELOG.md +++ CHANGELOG.md @@ -12,6 +12,5 @@ and this project adheres to [Semantic Versioning](http - Integration with libFuzzer's `LLVMFuzzerTestOneInput()`. - Integration with libFuzzer's `LLVMFuzzerCustomMutator()`. - Integration with libFuzzer's `FuzzedDataProvider`. -- libFuzzer custom mutator for Lua. - Examples with tests. - Documentation with usecases, API etc. blob - bb2884fff6f9a12fb76c1e1a9cada7fed3d7714f blob + da1edbdfd954ac1e81a5bc16f705f967cc0ba8bb --- CMakeLists.txt +++ CMakeLists.txt @@ -41,7 +41,6 @@ else() enable_testing() endif() -add_subdirectory(mutator) add_subdirectory(luzer) ## Install #################################################################### blob - dfc6a046bddd1ac47adcd3141dbd85d6963419e5 blob + cf5b696695274446967850402c83d7489de5d303 --- docs/api.md +++ docs/api.md @@ -83,15 +83,4 @@ AAAAAAAAA Learn more about grammar-based fuzzing in the [documentation](grammar_based_fuzzing.md). -### Custom mutator - -- `LLVMFuzzerCustomMutator(data, max_size, seed)` - optional user-provided - custom mutator. Mutates raw data in [`data`, `data` + size of `data`) inplace. - Returns the new size, which is not greater than `max_size`. Given the same - `seed` produces the same mutation. -- `LLVMFuzzerCustomCrossOver(data1, data2, max_size, seed)` - optional - user-provided custom cross-over function. Combines pieces of `data1` & `data2` - together into `out`. Returns the new size, which is not greater than `max_size`. - Should produce the same mutation given the same `seed`. - [libfuzzer-options-url]: https://llvm.org/docs/LibFuzzer.html#options blob - 08b64c368dcf8ed5dc24d8379bf0691a4ac8159c (mode 644) blob + /dev/null --- mutator/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -if(ENABLE_TESTING) - add_subdirectory(tests) -endif() - -install( - FILES - ${CMAKE_CURRENT_SOURCE_DIR}/mutator.c - ${CMAKE_CURRENT_SOURCE_DIR}/crossover.c - DESTINATION ${CMAKE_LUADIR}/mutator/ -) blob - 13d11e8d1aab431f7b02fa1956204953ba3d46f1 (mode 644) blob + /dev/null --- mutator/crossover.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: ISC - * - * Copyright 2022-2023, Sergey Bronnikov - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static const char *script_default = "./mutator.lua"; - -static size_t -luaL_custom_crossover(lua_State* L, const char *path, const char *func_name, - const uint8_t *data1, size_t size1, - const uint8_t *data2, size_t size2, - size_t max_out_size, unsigned int seed) -{ - luaL_dofile(L, path); - lua_getglobal(L, func_name); - if (!lua_isfunction(L, -1)) { - luaL_error(L, "'%s' is not a function", func_name); - } - lua_pushlstring(L, (const char*)data1, size1); - lua_pushlstring(L, (const char*)data2, size2); - lua_pushinteger(L, max_out_size); - lua_pushinteger(L, seed); - const int num_args = 4; - const int num_return_values = 2; - lua_pcall(L, num_args, num_return_values, 0); - - if (!lua_isnumber(L, -1)) { - luaL_error(L, "'%s' must return an integer value", func_name); - } - size_t ret_size = lua_tointeger(L, -1); - lua_pop(L, 1); - - if (!lua_isstring(L, -1)) { - luaL_error(L, "'%s' must return a string value", func_name); - } - data1 = (uint8_t *)lua_tostring(L, -1); /* FIXME */ - lua_pop(L, 1); - - return ret_size; -} - -size_t LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1, - const uint8_t *Data2, size_t Size2, - uint8_t *Out, size_t MaxOutSize, - unsigned int Seed) -{ - const char *script_env = "LIBFUZZER_LUA_SCRIPT"; - const char *script_func = "LLVMFuzzerCustomCrossOver"; - const char *script_path = getenv(script_env) ? : script_default; - - if (access(script_path, F_OK) != 0) { - fprintf(stderr, "Script (%s) is not accessible.\n", script_path); - _exit(1); - } - - lua_State* L = luaL_newstate(); - if (!L) { - fprintf(stderr, "Unable to create Lua state.\n"); - abort(); - } - luaL_openlibs(L); - size_t size = luaL_custom_crossover(L, script_path, script_func, - Data1, Size1, Data2, Size2, MaxOutSize, Seed); - lua_close(L); - - return size; -} blob - 3dccc65c42f2839278079e4b7542d39a01d98704 (mode 644) blob + /dev/null --- mutator/mutator.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * SPDX-License-Identifier: ISC - * - * Copyright 2022-2023, Sergey Bronnikov - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static const char *script_default = "./mutator.lua"; - -static size_t -luaL_custom_mutator(lua_State* L, const char *path, const char *func_name, - uint8_t *data, size_t size, - size_t max_size, unsigned int seed) -{ - luaL_dofile(L, path); - lua_getglobal(L, func_name); - if (!lua_isfunction(L, -1)) - luaL_error(L, "'%s' is not a function", func_name); - lua_pushlstring(L, (const char*)data, size); - lua_pushinteger(L, max_size); - lua_pushinteger(L, seed); - /* do the call (3 arguments, 2 results) */ - if (lua_pcall(L, 3, 2, 0) != 0) - luaL_error(L, "error running function '%s': %s", - func_name, lua_tostring(L, -1)); - - if (!lua_isnumber(L, -1)) { - luaL_error(L, "'%s' must return a number", func_name); - } - size_t ret_size = lua_tonumber(L, -1) - 1; - lua_pop(L, 1); - - if (!lua_isstring(L, -1)) { - luaL_error(L, "'%s' must return a string", func_name); - } - const char *res = lua_tolstring(L, -1, &ret_size); - lua_pop(L, 1); - - *data = *res; - - return ret_size; -} - -size_t -LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, - size_t MaxSize, unsigned int Seed) -{ - const char *script_env = "LIBFUZZER_LUA_SCRIPT"; - const char *script_func = "LLVMFuzzerCustomMutator"; - const char *script_path = getenv(script_env) ? : script_default; - - if (access(script_path, F_OK) != 0) { - fprintf(stderr, "Script (%s) is not accessible.\n", script_path); - _exit(1); - } - - lua_State* L = luaL_newstate(); - if (!L) { - fprintf(stderr, "Unable to create Lua state.\n"); - abort(); - } - luaL_openlibs(L); - size_t ret_size = luaL_custom_mutator(L, script_path, script_func, - Data, Size, MaxSize, Seed); - lua_close(L); - - return ret_size; -} blob - 3477cff2d884071d10e8811f359e9bc18e4e8b88 (mode 644) blob + /dev/null --- mutator/tests/CMakeLists.txt +++ /dev/null @@ -1,119 +0,0 @@ -add_executable(mutator_basic_test - ${PROJECT_SOURCE_DIR}/mutator/mutator.c - ${CMAKE_CURRENT_SOURCE_DIR}/mutator_basic_test.c) -target_include_directories(mutator_basic_test PRIVATE ${LUA_INCLUDE_DIR}) -target_link_libraries(mutator_basic_test PRIVATE ${LUA_LIBRARIES}) -target_compile_options(mutator_basic_test PUBLIC -Wall -Wextra -Wno-unused-parameter) - -add_executable(mutator_seed_test - ${PROJECT_SOURCE_DIR}/mutator/mutator.c - ${CMAKE_CURRENT_SOURCE_DIR}/mutator_seed_test.c) -target_include_directories(mutator_seed_test PRIVATE ${LUA_INCLUDE_DIR}) -target_link_libraries(mutator_seed_test PRIVATE ${LUA_LIBRARIES}) -target_compile_options(mutator_seed_test PUBLIC -Wall -Wextra -Wno-unused-parameter) - -add_executable(mutator_e2e_test - ${PROJECT_SOURCE_DIR}/mutator/mutator.c - ${CMAKE_CURRENT_SOURCE_DIR}/mutator_e2e_test.c) -target_include_directories(mutator_e2e_test PRIVATE ${LUA_INCLUDE_DIR}) -target_link_libraries(mutator_e2e_test PRIVATE ${LUA_LIBRARIES} -fsanitize=address,fuzzer) -target_compile_options(mutator_e2e_test PUBLIC -Wall -Wextra -Wno-unused-parameter) - -add_executable(crossover_basic_test - ${PROJECT_SOURCE_DIR}/mutator/crossover.c - ${CMAKE_CURRENT_SOURCE_DIR}/crossover_basic_test.c) -target_include_directories(crossover_basic_test PRIVATE ${LUA_INCLUDE_DIR}) -target_link_libraries(crossover_basic_test PRIVATE ${LUA_LIBRARIES}) -target_compile_options(crossover_basic_test PUBLIC -Wall -Wextra -Wno-unused-parameter) - -add_executable(crossover_seed_test - ${PROJECT_SOURCE_DIR}/mutator/crossover.c - ${CMAKE_CURRENT_SOURCE_DIR}/crossover_seed_test.c) -target_include_directories(crossover_seed_test PRIVATE ${LUA_INCLUDE_DIR}) -target_link_libraries(crossover_seed_test PRIVATE ${LUA_LIBRARIES}) -target_compile_options(crossover_seed_test PUBLIC -Wall -Wextra -Wno-unused-parameter) - -add_executable(crossover_e2e_test - ${PROJECT_SOURCE_DIR}/mutator/crossover.c - ${CMAKE_CURRENT_SOURCE_DIR}/crossover_e2e_test.c) -target_include_directories(crossover_e2e_test PRIVATE ${LUA_INCLUDE_DIR}) -target_link_libraries(crossover_e2e_test PRIVATE ${LUA_LIBRARIES} -fsanitize=address,fuzzer) -target_compile_options(crossover_e2e_test PUBLIC -Wall -Wextra -Wno-unused-parameter) - -add_test( - NAME mutator_basic_test - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/mutator_basic_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} -) -set_tests_properties(mutator_basic_test PROPERTIES - ENVIRONMENT "LIBFUZZER_LUA_SCRIPT=${CMAKE_CURRENT_SOURCE_DIR}/script_basic.lua" -) - -add_test( - NAME mutator_seed_test - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/mutator_seed_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} -) -set_tests_properties(mutator_seed_test PROPERTIES - ENVIRONMENT "LIBFUZZER_LUA_SCRIPT=${CMAKE_CURRENT_SOURCE_DIR}/script_seed.lua" -) - -add_test( - NAME mutator_e2e_test - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/mutator_e2e_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} -) -set_tests_properties(mutator_e2e_test PROPERTIES - ENVIRONMENT "LIBFUZZER_LUA_SCRIPT=${CMAKE_CURRENT_SOURCE_DIR}/script_e2e.lua" - PASS_REGULAR_EXPRESSION "BINGO: Found the target, exiting." - DISABLED True -) - -add_test( - NAME mutator_no_script_test - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/mutator_basic_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} -) -set_tests_properties(mutator_no_script_test PROPERTIES - ENVIRONMENT "LIBFUZZER_LUA_SCRIPT=unknown" - PASS_REGULAR_EXPRESSION "is not accessible" -) - -add_test( - NAME crossover_basic_test - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/crossover_basic_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} -) -set_tests_properties(crossover_basic_test PROPERTIES - ENVIRONMENT "LIBFUZZER_LUA_SCRIPT=${CMAKE_CURRENT_SOURCE_DIR}/script_basic.lua" -) - -add_test( - NAME crossover_seed_test - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/crossover_seed_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} -) -set_tests_properties(crossover_seed_test PROPERTIES - ENVIRONMENT "LIBFUZZER_LUA_SCRIPT=${CMAKE_CURRENT_SOURCE_DIR}/script_seed.lua" -) - -add_test( - NAME crossover_e2e_test - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/crossover_e2e_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} -) -set_tests_properties(crossover_e2e_test PROPERTIES - ENVIRONMENT "LIBFUZZER_LUA_SCRIPT=${CMAKE_CURRENT_SOURCE_DIR}/script_e2e.lua" - PASS_REGULAR_EXPRESSION "BINGO: Found the target, exiting." - DISABLED True -) - -add_test( - NAME crossover_no_script_test - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/crossover_basic_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} -) -set_tests_properties(crossover_no_script_test PROPERTIES - ENVIRONMENT "LIBFUZZER_LUA_SCRIPT=unknown" - PASS_REGULAR_EXPRESSION "is not accessible" -) blob - aa0cc6ce25dcaa6bc4869da6e2dae1f6a7bf8d85 (mode 644) blob + /dev/null --- mutator/tests/crossover_basic_test.c +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) - -#ifdef __cplusplus -extern "C" { -#endif - -size_t -LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1, - const uint8_t *Data2, size_t Size2, - uint8_t *Out, size_t MaxOutSize, - unsigned int Seed); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -static void -test_basic() -{ - uint8_t data[] = { 'L', 'U', 'A' }; - size_t size = COUNT_OF(data); - size_t max_size = size + 1; - size_t seed = 100; - size_t res = LLVMFuzzerCustomCrossOver(data, size, data, size, - NULL, max_size, seed); - assert(res != 0); - /* assert(strcmp((char *)data, "luzer") == 0); */ -} - -int main(void) -{ - test_basic(); -} blob - 0c3246676c84583417b2e3939acf59e4fb22bfcd (mode 644) blob + /dev/null --- mutator/tests/crossover_e2e_test.c +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include -#include -#include -#include -#include - -int -LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) -{ - assert(Data); - char *buf = calloc(Size, sizeof(char *)); - memcpy(buf, (char *)Data, Size); - buf[Size] = '\0'; - if (strcmp((char *)buf, "A") == 0) { - fprintf(stderr, "BINGO: Found the target, exiting.\n"); - _exit(1); - } - free(buf); - return 0; -} blob - 677e86a9cd6f4c6209cb51de1720e0358ccb6f22 (mode 644) blob + /dev/null --- mutator/tests/crossover_seed_test.c +++ /dev/null @@ -1,42 +0,0 @@ -#include -#include -#include -#include -#include - -#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) - -#ifdef __cplusplus -extern "C" { -#endif - -size_t -LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1, - const uint8_t *Data2, size_t Size2, - uint8_t *Out, size_t MaxOutSize, - unsigned int Seed); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -static void -test_seed() -{ - time_t t; - srand((unsigned) time(&t)); - - uint8_t data[] = { 'L', 'U', 'A' }; - size_t size = COUNT_OF(data); - size_t max_size = size; - size_t seed = rand(); - size_t res = LLVMFuzzerCustomCrossOver(data, size, data, size, - NULL, max_size, seed); - assert(res != 0); -} - -int -main(void) -{ - test_seed(); -} blob - b94a884c7deacd4128b697d59d97c00990a67b62 (mode 644) blob + /dev/null --- mutator/tests/mutator_basic_test.c +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) - -#ifdef __cplusplus -extern "C" { -#endif - -size_t -LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, - size_t MaxSize, unsigned int Seed); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -static void -test_basic() -{ - uint8_t data[] = { 'L', 'U', 'A' }; - size_t size = COUNT_OF(data); - size_t max_size = size + 1; - size_t seed = 0; - size_t res = LLVMFuzzerCustomMutator(data, size, max_size, seed); - assert(res != 0); - data[res] = '\0'; - assert(strcmp((char *)data, "XUA") == 0); -} - -int -main(void) -{ - test_basic(); -} blob - 0c3246676c84583417b2e3939acf59e4fb22bfcd (mode 644) blob + /dev/null --- mutator/tests/mutator_e2e_test.c +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include -#include -#include -#include -#include - -int -LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) -{ - assert(Data); - char *buf = calloc(Size, sizeof(char *)); - memcpy(buf, (char *)Data, Size); - buf[Size] = '\0'; - if (strcmp((char *)buf, "A") == 0) { - fprintf(stderr, "BINGO: Found the target, exiting.\n"); - _exit(1); - } - free(buf); - return 0; -} blob - 90dc199490f4912d502f71f75baa1f3b21c88b8b (mode 644) blob + /dev/null --- mutator/tests/mutator_seed_test.c +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include -#include -#include -#include - -#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) - -#ifdef __cplusplus -extern "C" { -#endif - -size_t -LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, - size_t MaxSize, unsigned int Seed); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -static void -test_seed() -{ - time_t t; - srand((unsigned) time(&t)); - - uint8_t data[] = { 'L', 'U', 'A' }; - size_t size = COUNT_OF(data); - size_t max_size = size; - size_t seed = rand(); - size_t res = LLVMFuzzerCustomMutator(data, size, max_size, seed); - assert(res != 0); -} - -int -main(void) -{ - test_seed(); -} blob - 5534b285ce376d966905cea86d28413a412c74f7 (mode 644) blob + /dev/null --- mutator/tests/script_basic.lua +++ /dev/null @@ -1,35 +0,0 @@ -function LLVMFuzzerCustomMutator(data, max_size, seed) -- luacheck: ignore - assert(type(data) == "string") - assert(data == "LUA") - - assert(type(max_size) == "number") - assert(max_size == #data + 1) - - assert(type(seed) == "number") - assert(seed ~= nil) - assert(seed == 0) - - local b = {} - data:gsub(".", function(c) table.insert(b, c) end) - b[1] = "X" - local buf = table.concat(b, "") - - return buf, #buf -end - -function LLVMFuzzerCustomCrossOver(data1, data2, max_size, seed) -- luacheck: ignore - assert(type(data1) == "string") - assert(data1 == "LUA") - - assert(type(data2) == "string") - assert(data2 == "LUA") - - assert(type(max_size) == "number") - - assert(type(seed) == "number") - assert(seed ~= nil) - - local buf = "luzer" - - return buf, #buf -end blob - 21f0aa67c0286ba0fcf1d670bb720c25fd321322 (mode 644) blob + /dev/null --- mutator/tests/script_e2e.lua +++ /dev/null @@ -1,7 +0,0 @@ -function LLVMFuzzerCustomMutator(data, max_size, seed) -- luacheck: ignore - return string.rep("A", #data), #data -end - -function LLVMFuzzerCustomCrossOver(data1, data2, max_size, seed) -- luacheck: ignore - return "", 0 -end blob - b80ad15623c729591f0c9a5f34f758a18f870066 (mode 644) blob + /dev/null --- mutator/tests/script_seed.lua +++ /dev/null @@ -1,11 +0,0 @@ -local set_seed = require("math").randomseed - -function LLVMFuzzerCustomMutator(data, max_size, seed) -- luacheck: ignore - set_seed(seed) - return data .. "xxx", 10 -end - -function LLVMFuzzerCustomCrossOver(data1, data2, max_size, seed) -- luacheck: ignore - set_seed(seed) - return data1 .. "xxx", 10 -end