Skip to content
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

cmake: fix searching Clang RT #9

Merged
merged 9 commits into from
Jul 10, 2024
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
4 changes: 2 additions & 2 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- uses: actions/checkout@v3

- name: Setup common packages
run: sudo apt install -y clang-14 libclang-common-14-dev
run: sudo apt install -y clang-15 libclang-common-15-dev

- name: Setup Lua 5.1 packages
run: sudo apt install -y lua5.1 liblua5.1-0-dev
Expand All @@ -42,7 +42,7 @@ jobs:
if: ${{ matrix.LIBLUA == '5.4' }}

- name: Running CMake
run: cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_TESTING=ON -S . -B build
run: cmake -DCMAKE_C_COMPILER=clang-15 -DCMAKE_CXX_COMPILER=clang++-15 -DENABLE_TESTING=ON -S . -B build

- name: Building
run: cmake --build build --parallel $(nproc)
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- Disable coverage instrumentation of internal functions (#11).
- Add missed newlines to messages.

### Fixed

- Fix searching Clang RT.
- Stack overflow due to recursive traceback calls.
4 changes: 3 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ project(luzer
VERSION "1.0.0"
)

set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
include(SetClangRTLib)

find_package(Lua 5.1 REQUIRED)
find_package(LLVM REQUIRED CONFIG)
find_library(LIBRT rt)

set(LUA_NAME "lua${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}")
find_program(LUA_EXECUTABLE "${LUA_NAME}")
Expand Down
2 changes: 1 addition & 1 deletion CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"name": "default",
"configurePreset": "default",
"output": {"outputOnFailure": true},
"execution": {"noTestsAction": "error", "stopOnFailure": true}
"execution": {"noTestsAction": "error", "stopOnFailure": true, "timeout": 10}
}
],
"workflowPresets": [
Expand Down
40 changes: 40 additions & 0 deletions cmake/SetClangRTLib.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# The function sets the given variable in a parent scope to a
# value in FUZZER_NO_MAIN_LIBRARY environment variable if it
# is set. Otherwise the value with path to a directory with
# libclang_rt.fuzzer_no_main library is composed manually.
# Function raises a fatal message if C compiler is not Clang.
#
# $ clang-15 -print-file-name=libclang_rt.fuzzer_no_main-x86_64.a
# $ /usr/lib/llvm-15/lib/clang/15.0.7/lib/linux/libclang_rt.fuzzer_no_main-x86_64.a
#
# On Linux installations libFuzzer library is typically located at:
#
# /usr/lib/<llvm-version>/lib/clang/<clang-version>/lib/linux/libclang_rt.fuzzer_no_main-<architecture>.a
#
# 1. https://llvm.org/docs/LibFuzzer.html#using-libfuzzer-as-a-library

function(SetFuzzerNoMainLibPath outvar)
if (NOT CMAKE_C_COMPILER_ID STREQUAL "Clang")
message(FATAL_ERROR "C compiler is not a Clang")
endif ()

if (CMAKE_SIZEOF_VOID_P EQUAL 4)
set(ARCH "i386")
elseif (CMAKE_SIZEOF_VOID_P EQUAL 8)
set(ARCH "x86_64")
else ()
message(FATAL_ERROR "Unsupported architecture.")
endif ()

set(LIB_FUZZER_NO_MAIN "libclang_rt.fuzzer_no_main-${ARCH}.a")
execute_process(COMMAND ${CMAKE_C_COMPILER} "-print-file-name=${LIB_FUZZER_NO_MAIN}"
RESULT_VARIABLE CMD_ERROR
OUTPUT_VARIABLE CMD_OUTPUT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (CMD_ERROR)
message(FATAL_ERROR "${CMD_ERROR}")
endif ()
set(${outvar} ${CMD_OUTPUT} PARENT_SCOPE)
message(STATUS "[SetClangRTLib] ${outvar} is ${CMD_OUTPUT}")
endfunction()
19 changes: 3 additions & 16 deletions luzer/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,17 +1,4 @@
# Locate compiler-rt libraries.
# Location is LLVM_LIBRARY_DIRS/clang/<version>/lib/<OS>/,
# for example LLVM_LIBRARY_DIRS/clang/4.0.0/lib/darwin/.
#
# See https://llvm.org/docs/LibFuzzer.html#using-libfuzzer-as-a-library

set(LLVM_BASE ${LLVM_LIBRARY_DIRS}/clang/${LLVM_PACKAGE_VERSION})
string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} OS_NAME)
set(LIBCLANG_RT ${LLVM_BASE}/lib/${OS_NAME}/libclang_rt.fuzzer_no_main-x86_64.a)
if(EXISTS ${LIBCLANG_RT})
message(STATUS "Found libclang_rt ${LIBCLANG_RT}")
else()
message(FATAL_ERROR "libclang_rt is not found")
endif()
SetFuzzerNoMainLibPath(FUZZER_NO_MAIN_LIBRARY)

configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/version.c
Expand All @@ -20,6 +7,7 @@ configure_file(
)

set(LUZER_SOURCES luzer.c
compat.c
fuzzed_data_provider.cc
tracer.c
counters.c
Expand All @@ -31,9 +19,8 @@ target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
)
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
${LUA_LIBRARIES}
${LIBRT}
${LIBCLANG_RT}
-fsanitize=fuzzer-no-link
${FUZZER_NO_MAIN_LIBRARY}
)
target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE
-D_FORTIFY_SOURCE=2
Expand Down
151 changes: 151 additions & 0 deletions luzer/compat.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM == 501

static int
countlevels(lua_State *L) {
lua_Debug ar;
int li = 1, le = 1;
/* Find an upper bound. */
while (lua_getstack(L, le, &ar))
{ li = le; le *= 2; }
/* Do a binary search. */
while (li < le) {
int m = (li + le) / 2;
if (lua_getstack(L, m, &ar))
li = m + 1;
else
le = m;
}
return le - 1;
}

static int
findfield(lua_State *L, int objidx, int level) {
if (level == 0 || !lua_istable(L, -1))
/* Not found. */
return 0;

/* Start 'next' loop. */
lua_pushnil(L);
/* For each pair in table. */
while (lua_next(L, -2)) {
/* Ignore non-string keys. */
if (lua_type(L, -2) == LUA_TSTRING) {
/* Found an object? */
if (lua_rawequal(L, objidx, -1)) {
/* Remove value (but keep name). */
lua_pop(L, 1);
return 1;
}
/* Try recursively. */
else if (findfield(L, objidx, level - 1)) {
/* Remove table (but keep name). */
lua_remove(L, -2);
lua_pushliteral(L, ".");
/* Place '.' between the two names. */
lua_insert(L, -2);
lua_concat(L, 3);
return 1;
}
}
/* Remove value. */
lua_pop(L, 1);
}
/* Not found. */
return 0;
}

int
lua_absindex(lua_State *L, int i) {
if (i < 0 && i > LUA_REGISTRYINDEX)
i += lua_gettop(L) + 1;
return i;
}

void
lua_copy(lua_State *L, int from, int to) {
int abs_to = lua_absindex(L, to);
luaL_checkstack(L, 1, "not enough stack slots");
lua_pushvalue(L, from);
lua_replace(L, abs_to);
}

static int
pushglobalfuncname(lua_State *L, lua_Debug *ar) {
int top = lua_gettop(L);
/* Push function. */
lua_getinfo(L, "f", ar);
lua_pushvalue(L, LUA_GLOBALSINDEX);
if (findfield(L, top + 1, 2)) {
/* Move name to proper place. */
lua_copy(L, -1, top + 1);
/* Remove pushed values. */
lua_pop(L, 2);
return 1;
}
else {
/* Remove function and global table. */
lua_settop(L, top);
return 0;
}
}

static void
pushfuncname(lua_State *L, lua_Debug *ar) {
/* Is there a name? */
if (*ar->namewhat != '\0')
lua_pushfstring(L, "function " LUA_QS, ar->name);
else if (*ar->what == 'm') /* Main? */
lua_pushliteral(L, "main chunk");
else if (*ar->what == 'C') {
if (pushglobalfuncname(L, ar)) {
lua_pushfstring(L, "function " LUA_QS, lua_tostring(L, -1));
/* Remove name. */
lua_remove(L, -2);
}
else
lua_pushliteral(L, "?");
}
else
lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined);
}

/* Size of the first part of the stack. */
#define LEVELS1 12
/* Size of the second part of the stack. */
#define LEVELS2 10

void
luaL_traceback(lua_State *L, lua_State *L1,
const char *msg, int level) {
lua_Debug ar;
int top = lua_gettop(L);
int numlevels = countlevels(L1);
int mark = (numlevels > LEVELS1 + LEVELS2) ? LEVELS1 : 0;
if (msg) lua_pushfstring(L, "%s\n", msg);
lua_pushliteral(L, "stack traceback:");
while (lua_getstack(L1, level++, &ar)) {
/* Too many levels? */
if (level == mark) {
/* Add a '...'. */
lua_pushliteral(L, "\n\t...");
/* And skip to last ones. */
level = numlevels - LEVELS2;
}
else {
lua_getinfo(L1, "Slnt", &ar);
lua_pushfstring(L, "\n\t%s:", ar.short_src);
if (ar.currentline > 0)
lua_pushfstring(L, "%d:", ar.currentline);
lua_pushliteral(L, " in ");
pushfuncname(L, &ar);
lua_concat(L, lua_gettop(L) - top);
}
}
lua_concat(L, lua_gettop(L) - top);
}

#endif
7 changes: 7 additions & 0 deletions luzer/compat.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#ifndef LUZER_COMPAT_H_
#define LUZER_COMPAT_H_

void luaL_traceback(lua_State *L, lua_State *L1,
const char *msg, int level);

#endif // LUZER_COMPAT_H_
Loading
Loading