Compare commits
4
Commits
309367a91e
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f40ffaa6f7
|
||
|
|
d7bcd64130
|
||
|
|
af888258f4
|
||
|
|
914fa6e5a7
|
+374
-6
@@ -9,6 +9,331 @@ set(CMAKE_C_STANDARD 23)
|
|||||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_C_EXTENSIONS OFF)
|
set(CMAKE_C_EXTENSIONS OFF)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# Build configuration
|
||||||
|
#
|
||||||
|
# cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug # -Og -g3, ASan + UBSan
|
||||||
|
# cmake -S . -B build-strict -DCMAKE_BUILD_TYPE=Strict # Debug plus -Werror
|
||||||
|
# cmake -S . -B build-analyzer -DCMAKE_BUILD_TYPE=Analyzer # -Og -g3, GCC -fanalyzer
|
||||||
|
# cmake -S . -B build-release -DCMAKE_BUILD_TYPE=Release # -O3, _FORTIFY_SOURCE
|
||||||
|
#
|
||||||
|
# Debug finds memory bugs at run time, Analyzer finds them at compile time,
|
||||||
|
# Release survives them. Strict is Debug with warnings promoted to errors — the
|
||||||
|
# configuration CI should gate on, kept separate so a new warning never blocks
|
||||||
|
# someone mid-debugging. The warning set is identical in all four; only the
|
||||||
|
# instrumentation and the error policy differ.
|
||||||
|
#
|
||||||
|
# Debug and Analyzer are separate configurations rather than one Debug build
|
||||||
|
# with everything switched on, because the two tools actively interfere: ASan's
|
||||||
|
# instrumentation inflates the CFG enough that -fanalyzer exhausts its
|
||||||
|
# exploration budget and silently stops reporting. Measured on
|
||||||
|
# src/tcpd/tcpserver.c with GCC 16 — -fanalyzer alone reports the fd leak in
|
||||||
|
# TcpServer_Init, -fanalyzer plus ASan reports nothing at all.
|
||||||
|
#
|
||||||
|
# ThreadSanitizer is deliberately absent: it cannot be combined with ASan, so it
|
||||||
|
# would need a third configuration of its own.
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
set(SKALACOIN_CUSTOM_CONFIGS Strict Analyzer)
|
||||||
|
set(SKALACOIN_BUILD_TYPES Debug Strict Analyzer Release RelWithDebInfo MinSizeRel)
|
||||||
|
|
||||||
|
get_property(SKALACOIN_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||||
|
if(SKALACOIN_MULTI_CONFIG)
|
||||||
|
foreach(_cfg IN LISTS SKALACOIN_CUSTOM_CONFIGS)
|
||||||
|
if(NOT "${_cfg}" IN_LIST CMAKE_CONFIGURATION_TYPES)
|
||||||
|
list(APPEND CMAKE_CONFIGURATION_TYPES ${_cfg})
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
elseif(NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type" FORCE)
|
||||||
|
message(STATUS "No CMAKE_BUILD_TYPE specified; defaulting to Debug")
|
||||||
|
endif()
|
||||||
|
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${SKALACOIN_BUILD_TYPES})
|
||||||
|
|
||||||
|
# Strict and Analyzer are custom configurations, so CMake has no built-in flags
|
||||||
|
# for them. Both inherit Debug's, and imported targets (OpenSSL::Crypto,
|
||||||
|
# CURL::libcurl) need a mapping because they only ship Debug/Release/NOCONFIG.
|
||||||
|
foreach(_cfg IN LISTS SKALACOIN_CUSTOM_CONFIGS)
|
||||||
|
string(TOUPPER ${_cfg} _cfg_upper)
|
||||||
|
foreach(_lang C CXX)
|
||||||
|
set(CMAKE_${_lang}_FLAGS_${_cfg_upper} "${CMAKE_${_lang}_FLAGS_DEBUG}"
|
||||||
|
CACHE STRING "Flags used by the ${_lang} compiler during ${_cfg} builds.")
|
||||||
|
mark_as_advanced(CMAKE_${_lang}_FLAGS_${_cfg_upper})
|
||||||
|
endforeach()
|
||||||
|
foreach(_linker EXE SHARED MODULE STATIC)
|
||||||
|
set(CMAKE_${_linker}_LINKER_FLAGS_${_cfg_upper} "${CMAKE_${_linker}_LINKER_FLAGS_DEBUG}"
|
||||||
|
CACHE STRING "Flags used by the linker during ${_cfg} builds.")
|
||||||
|
mark_as_advanced(CMAKE_${_linker}_LINKER_FLAGS_${_cfg_upper})
|
||||||
|
endforeach()
|
||||||
|
set(CMAKE_MAP_IMPORTED_CONFIG_${_cfg_upper} Debug "" Release RelWithDebInfo)
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Debug, Strict and Analyzer share the developer flag set (-Og -g3, warnings).
|
||||||
|
set(SKALACOIN_IS_DEBUGLIKE "$<OR:$<CONFIG:Debug>,$<CONFIG:Strict>,$<CONFIG:Analyzer>>")
|
||||||
|
# Debug and Strict are the runtime-instrumented pair; Analyzer must stay clean
|
||||||
|
# of sanitizers or -fanalyzer goes quiet (see above).
|
||||||
|
set(SKALACOIN_IS_SANITIZED "$<OR:$<CONFIG:Debug>,$<CONFIG:Strict>>")
|
||||||
|
|
||||||
|
# The Strict configuration always errors on warnings; this option additionally
|
||||||
|
# promotes them in Debug and Analyzer. OFF until src/ is clean under the warning
|
||||||
|
# set below — until then, build Strict when you want the gate.
|
||||||
|
option(SKALACOIN_WERROR "Debug/Analyzer: treat warnings as errors (always on in Strict)" OFF)
|
||||||
|
option(SKALACOIN_ENABLE_SANITIZERS "Debug config: AddressSanitizer + UndefinedBehaviorSanitizer" ON)
|
||||||
|
option(SKALACOIN_ENABLE_ANALYZER "Analyzer config: the compiler's static analyzer (GCC -fanalyzer)" ON)
|
||||||
|
|
||||||
|
# How much of the control-flow path -fanalyzer prints per report. 1 lists only
|
||||||
|
# the state transitions (opened here / first close here / leaks here), which is
|
||||||
|
# what you want while triaging; raise it when a report needs the branch-by-branch
|
||||||
|
# path that explains how it got there. GCC silently accepts out-of-range values,
|
||||||
|
# so validate here instead.
|
||||||
|
set(ANALYZER_VERBOSITY 1 CACHE STRING "Analyzer config: -fanalyzer path detail, 0 (terse) to 5 (full)")
|
||||||
|
set_property(CACHE ANALYZER_VERBOSITY PROPERTY STRINGS 0 1 2 3 4 5)
|
||||||
|
if(NOT ANALYZER_VERBOSITY MATCHES "^[0-5]$")
|
||||||
|
message(FATAL_ERROR "ANALYZER_VERBOSITY must be an integer from 0 to 5, got '${ANALYZER_VERBOSITY}'")
|
||||||
|
endif()
|
||||||
|
option(SKALACOIN_ENABLE_HARDENING "All configs: stack protector, _FORTIFY_SOURCE, RELRO/NOW, CFI" ON)
|
||||||
|
option(SKALACOIN_ENABLE_LTO "Optimized configs: link-time optimization" OFF)
|
||||||
|
|
||||||
|
include(CheckCCompilerFlag)
|
||||||
|
include(CheckLinkerFlag)
|
||||||
|
|
||||||
|
# Most of the interesting memory diagnostics below only exist from a certain
|
||||||
|
# GCC/Clang version onwards, and several are architecture specific. Probe every
|
||||||
|
# flag instead of gating on compiler version, so an older or foreign toolchain
|
||||||
|
# silently gets the subset it understands rather than failing to configure.
|
||||||
|
#
|
||||||
|
# A flag the driver merely tolerates is not a flag that does anything (Clang
|
||||||
|
# accepts -fstack-clash-protection on arm64 and then ignores it), so the probe
|
||||||
|
# promotes "argument unused" to an error where the compiler supports that.
|
||||||
|
check_c_compiler_flag(-Werror=unused-command-line-argument SKALACOIN_HAS_WERROR_UNUSED_ARG)
|
||||||
|
if(SKALACOIN_HAS_WERROR_UNUSED_ARG)
|
||||||
|
set(SKALACOIN_FLAG_PROBE_STRICT "-Werror=unused-command-line-argument")
|
||||||
|
else()
|
||||||
|
set(SKALACOIN_FLAG_PROBE_STRICT "")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
function(skalacoin_append_supported_c_flags out_var)
|
||||||
|
set(_accepted ${${out_var}})
|
||||||
|
set(CMAKE_REQUIRED_FLAGS "${SKALACOIN_FLAG_PROBE_STRICT}")
|
||||||
|
foreach(_flag IN LISTS ARGN)
|
||||||
|
string(MAKE_C_IDENTIFIER "SKALACOIN_HAS_CFLAG_${_flag}" _cache_var)
|
||||||
|
check_c_compiler_flag("${_flag}" ${_cache_var})
|
||||||
|
if(${_cache_var})
|
||||||
|
list(APPEND _accepted "${_flag}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
set(${out_var} "${_accepted}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Instrumentation such as -fsanitize=address has to reach the linker as well,
|
||||||
|
# and is only usable if the matching runtime library actually exists, so the
|
||||||
|
# probe hands the flag to both halves of the try-compile.
|
||||||
|
#
|
||||||
|
# Flags are also probed cumulatively, in the order given: some are only legal in
|
||||||
|
# the presence of an earlier one (-fsanitize=pointer-compare is rejected unless
|
||||||
|
# -fsanitize=address is already on the command line), and probing it alone would
|
||||||
|
# quietly drop it.
|
||||||
|
function(skalacoin_append_supported_instrument_flags out_var)
|
||||||
|
set(_accepted ${${out_var}})
|
||||||
|
set(_context "")
|
||||||
|
foreach(_flag IN LISTS ARGN)
|
||||||
|
string(MAKE_C_IDENTIFIER "SKALACOIN_HAS_INSTRUMENT_${_flag}" _cache_var)
|
||||||
|
string(JOIN " " CMAKE_REQUIRED_FLAGS ${SKALACOIN_FLAG_PROBE_STRICT} ${_context})
|
||||||
|
set(CMAKE_REQUIRED_LINK_OPTIONS ${_context} "${_flag}")
|
||||||
|
check_c_compiler_flag("${_flag}" ${_cache_var})
|
||||||
|
if(${_cache_var})
|
||||||
|
list(APPEND _accepted "${_flag}")
|
||||||
|
list(APPEND _context "${_flag}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
set(${out_var} "${_accepted}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(skalacoin_append_supported_link_flags out_var)
|
||||||
|
set(_accepted ${${out_var}})
|
||||||
|
foreach(_flag IN LISTS ARGN)
|
||||||
|
string(MAKE_C_IDENTIFIER "SKALACOIN_HAS_LDFLAG_${_flag}" _cache_var)
|
||||||
|
check_linker_flag(C "${_flag}" ${_cache_var})
|
||||||
|
if(${_cache_var})
|
||||||
|
list(APPEND _accepted "${_flag}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
set(${out_var} "${_accepted}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Warnings for our own C code; the per-config flag sets are applied through
|
||||||
|
# generator expressions so multi-config generators (Xcode, VS) work too.
|
||||||
|
set(SKALACOIN_C_WARNINGS "")
|
||||||
|
set(SKALACOIN_C_FLAGS_DEBUGLIKE "")
|
||||||
|
set(SKALACOIN_C_FLAGS_OPTIMIZED "")
|
||||||
|
# The two instrumentation sets, each owned by one configuration and never both.
|
||||||
|
set(SKALACOIN_SANITIZER_FLAGS "")
|
||||||
|
set(SKALACOIN_ANALYZER_FLAGS "")
|
||||||
|
# Instrumentation that must be handed to the linker as well, and that also
|
||||||
|
# covers the vendored code we link in.
|
||||||
|
set(SKALACOIN_INSTRUMENT_COMPILE "")
|
||||||
|
set(SKALACOIN_INSTRUMENT_LINK "")
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
set(SKALACOIN_WERROR_FLAG /WX)
|
||||||
|
list(APPEND SKALACOIN_C_WARNINGS /W4 /permissive- /sdl)
|
||||||
|
list(APPEND SKALACOIN_C_FLAGS_DEBUGLIKE /Od /RTC1 /GS)
|
||||||
|
list(APPEND SKALACOIN_C_FLAGS_OPTIMIZED /O2 /GS /guard:cf)
|
||||||
|
if(SKALACOIN_WERROR)
|
||||||
|
list(APPEND SKALACOIN_C_FLAGS_DEBUGLIKE ${SKALACOIN_WERROR_FLAG})
|
||||||
|
endif()
|
||||||
|
if(SKALACOIN_ENABLE_ANALYZER)
|
||||||
|
list(APPEND SKALACOIN_ANALYZER_FLAGS /analyze)
|
||||||
|
endif()
|
||||||
|
if(SKALACOIN_ENABLE_SANITIZERS)
|
||||||
|
list(APPEND SKALACOIN_SANITIZER_FLAGS /fsanitize=address)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(SKALACOIN_WERROR_FLAG -Werror)
|
||||||
|
# Portable warning set, memory-safety first.
|
||||||
|
skalacoin_append_supported_c_flags(SKALACOIN_C_WARNINGS
|
||||||
|
-Wall
|
||||||
|
-Wextra
|
||||||
|
-Wpedantic
|
||||||
|
-Wshadow
|
||||||
|
-Wconversion
|
||||||
|
-Wsign-conversion
|
||||||
|
-Wcast-qual
|
||||||
|
-Wcast-align
|
||||||
|
-Wstrict-prototypes
|
||||||
|
-Wmissing-prototypes
|
||||||
|
-Wold-style-definition
|
||||||
|
-Wbad-function-cast
|
||||||
|
-Wwrite-strings
|
||||||
|
-Wformat=2
|
||||||
|
-Wnull-dereference
|
||||||
|
-Wdouble-promotion
|
||||||
|
-Wfloat-equal # consensus code must stay integer-only
|
||||||
|
-Wvla
|
||||||
|
-Wstack-protector
|
||||||
|
-Wpointer-arith
|
||||||
|
-Wundef
|
||||||
|
-Winit-self
|
||||||
|
-Wmissing-include-dirs
|
||||||
|
)
|
||||||
|
|
||||||
|
if(CMAKE_C_COMPILER_ID MATCHES "GNU")
|
||||||
|
# GCC-only diagnostics. The -W*=N forms ask for the strictest level:
|
||||||
|
# more false positives, but they catch out-of-bounds writes, dangling
|
||||||
|
# pointers and double frees that -Wall/-Wextra walk straight past.
|
||||||
|
skalacoin_append_supported_c_flags(SKALACOIN_C_WARNINGS
|
||||||
|
-Warray-bounds=2
|
||||||
|
-Wstringop-overflow=4
|
||||||
|
-Wstringop-truncation
|
||||||
|
-Wformat-overflow=2
|
||||||
|
-Wformat-truncation=2
|
||||||
|
-Wuse-after-free=3
|
||||||
|
-Wdangling-pointer=2
|
||||||
|
-Wfree-nonheap-object
|
||||||
|
-Walloc-zero
|
||||||
|
-Walloca
|
||||||
|
-Wduplicated-cond
|
||||||
|
-Wduplicated-branches
|
||||||
|
-Wlogical-op
|
||||||
|
-Wjump-misses-init
|
||||||
|
-Wtrampolines
|
||||||
|
-Wflex-array-member-not-at-end
|
||||||
|
)
|
||||||
|
# -fanalyzer is a whole-path symbolic execution pass (leaks, double
|
||||||
|
# free, use-after-free, NULL derefs across function boundaries).
|
||||||
|
if(SKALACOIN_ENABLE_ANALYZER)
|
||||||
|
skalacoin_append_supported_c_flags(SKALACOIN_ANALYZER_FLAGS
|
||||||
|
-fanalyzer-verbosity=${ANALYZER_VERBOSITY}
|
||||||
|
-fanalyzer
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||||
|
# Clang-only diagnostics. Clang has no in-compiler equivalent of
|
||||||
|
# -fanalyzer, so the Analyzer config is simply an uninstrumented Debug
|
||||||
|
# build here — which is exactly the base `scan-build cmake --build
|
||||||
|
# build-analyzer` wants.
|
||||||
|
skalacoin_append_supported_c_flags(SKALACOIN_C_WARNINGS
|
||||||
|
-Warray-bounds-pointer-arithmetic
|
||||||
|
-Wconditional-uninitialized
|
||||||
|
-Wshift-sign-overflow
|
||||||
|
-Wassign-enum
|
||||||
|
-Wcomma
|
||||||
|
-Wloop-analysis
|
||||||
|
-Wthread-safety
|
||||||
|
-Wover-aligned
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# -Og keeps the code steppable while still running the optimizer passes
|
||||||
|
# that -Wmaybe-uninitialized / -Wstringop-* rely on; at -O0 those warnings
|
||||||
|
# go quiet. -fno-omit-frame-pointer buys readable sanitizer backtraces.
|
||||||
|
skalacoin_append_supported_c_flags(SKALACOIN_C_FLAGS_DEBUGLIKE
|
||||||
|
-Og
|
||||||
|
-g3
|
||||||
|
-fno-omit-frame-pointer
|
||||||
|
)
|
||||||
|
if(SKALACOIN_WERROR)
|
||||||
|
list(APPEND SKALACOIN_C_FLAGS_DEBUGLIKE ${SKALACOIN_WERROR_FLAG})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(SKALACOIN_ENABLE_HARDENING)
|
||||||
|
skalacoin_append_supported_c_flags(SKALACOIN_C_FLAGS_DEBUGLIKE
|
||||||
|
-fstack-protector-strong
|
||||||
|
)
|
||||||
|
# _FORTIFY_SOURCE needs an optimized build to see through the buffer
|
||||||
|
# sizes, and it fights ASan's interceptors, so it is Release-only.
|
||||||
|
skalacoin_append_supported_c_flags(SKALACOIN_C_FLAGS_OPTIMIZED
|
||||||
|
-U_FORTIFY_SOURCE
|
||||||
|
-D_FORTIFY_SOURCE=3
|
||||||
|
-fstack-protector-strong
|
||||||
|
-fstack-clash-protection
|
||||||
|
)
|
||||||
|
skalacoin_append_supported_instrument_flags(SKALACOIN_INSTRUMENT_COMPILE
|
||||||
|
-fcf-protection=full # x86_64 CET
|
||||||
|
-mbranch-protection=standard # aarch64 BTI/PAC
|
||||||
|
)
|
||||||
|
skalacoin_append_supported_link_flags(SKALACOIN_INSTRUMENT_LINK
|
||||||
|
"LINKER:-z,relro"
|
||||||
|
"LINKER:-z,now"
|
||||||
|
"LINKER:-z,noexecstack"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(SKALACOIN_ENABLE_SANITIZERS)
|
||||||
|
# Everything that composes with ASan. LeakSanitizer is not listed
|
||||||
|
# because ASan already includes it where it is supported, and
|
||||||
|
# -fsanitize=leak cannot be combined with -fsanitize=address.
|
||||||
|
# -fno-sanitize-recover makes UB abort instead of printing and
|
||||||
|
# continuing, so a bad shift or overflow cannot be ignored in CI.
|
||||||
|
# pointer-compare/pointer-subtract additionally need
|
||||||
|
# ASAN_OPTIONS=detect_invalid_pointer_pairs=2 at run time.
|
||||||
|
skalacoin_append_supported_instrument_flags(SKALACOIN_SANITIZER_FLAGS
|
||||||
|
-fsanitize=address
|
||||||
|
-fsanitize-address-use-after-scope
|
||||||
|
-fsanitize=pointer-compare
|
||||||
|
-fsanitize=pointer-subtract
|
||||||
|
-fsanitize=undefined
|
||||||
|
-fsanitize=bounds-strict
|
||||||
|
-fno-sanitize-recover=undefined
|
||||||
|
-fno-omit-frame-pointer
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Each instrumentation set belongs to exactly one configuration. Keeping them in
|
||||||
|
# separate configs is the point of the Analyzer build, so never emit both.
|
||||||
|
if(SKALACOIN_SANITIZER_FLAGS)
|
||||||
|
list(APPEND SKALACOIN_INSTRUMENT_COMPILE "$<${SKALACOIN_IS_SANITIZED}:${SKALACOIN_SANITIZER_FLAGS}>")
|
||||||
|
list(APPEND SKALACOIN_INSTRUMENT_LINK "$<${SKALACOIN_IS_SANITIZED}:${SKALACOIN_SANITIZER_FLAGS}>")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(SKALACOIN_ENABLE_LTO)
|
||||||
|
include(CheckIPOSupported)
|
||||||
|
check_ipo_supported(RESULT SKALACOIN_IPO_SUPPORTED OUTPUT SKALACOIN_IPO_ERROR)
|
||||||
|
if(NOT SKALACOIN_IPO_SUPPORTED)
|
||||||
|
message(WARNING "LTO requested but unsupported by this toolchain: ${SKALACOIN_IPO_ERROR}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
include(FetchContent)
|
include(FetchContent)
|
||||||
|
|
||||||
@@ -146,6 +471,11 @@ if(SKALACOIN_ENABLE_AUTOLYKOS2_REF)
|
|||||||
|
|
||||||
add_library(autolykos2_ref STATIC ${AUTOLYKOS2_REF_SOURCES})
|
add_library(autolykos2_ref STATIC ${AUTOLYKOS2_REF_SOURCES})
|
||||||
target_include_directories(autolykos2_ref PRIVATE ${AUTOLYKOS2_REF_BASE}/include)
|
target_include_directories(autolykos2_ref PRIVATE ${AUTOLYKOS2_REF_BASE}/include)
|
||||||
|
# Vendored code gets the instrumentation but not our warning set: sanitizers
|
||||||
|
# only see a bug if the translation unit that owns the memory is compiled
|
||||||
|
# with them, and this library allocates buffers that our code touches.
|
||||||
|
target_compile_options(autolykos2_ref PRIVATE "${SKALACOIN_INSTRUMENT_COMPILE}")
|
||||||
|
target_link_options(autolykos2_ref PRIVATE "${SKALACOIN_INSTRUMENT_LINK}")
|
||||||
# Upstream source uses `malloc/free/exit/EXIT_FAILURE` without including
|
# Upstream source uses `malloc/free/exit/EXIT_FAILURE` without including
|
||||||
# stdlib headers in some C++ translation units. AppleClang can compile this,
|
# stdlib headers in some C++ translation units. AppleClang can compile this,
|
||||||
# while Linux Clang fails. Force-include stdlib.h for C++ in this vendored lib.
|
# while Linux Clang fails. Force-include stdlib.h for C++ in this vendored lib.
|
||||||
@@ -186,7 +516,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
|||||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||||
|
|
||||||
foreach(OUTPUTCONFIG DEBUG RELEASE RELWITHDEBINFO MINSIZEREL)
|
foreach(OUTPUTCONFIG DEBUG STRICT ANALYZER RELEASE RELWITHDEBINFO MINSIZEREL)
|
||||||
string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG_UPPER)
|
string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG_UPPER)
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR}/bin)
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR}/bin)
|
||||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR}/lib)
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR}/lib)
|
||||||
@@ -220,12 +550,35 @@ endif()
|
|||||||
target_include_directories(node PRIVATE
|
target_include_directories(node PRIVATE
|
||||||
${PROJECT_SOURCE_DIR}/include
|
${PROJECT_SOURCE_DIR}/include
|
||||||
)
|
)
|
||||||
target_compile_options(node PRIVATE
|
# khash is vendored third-party code we cannot fix, and it accounts for half the
|
||||||
-Wall
|
# warnings under Strict. SYSTEM turns -I into -isystem, which suppresses
|
||||||
-Wextra
|
# diagnostics from headers found through it. It needs its own search path: via
|
||||||
-Wpedantic
|
# ${PROJECT_SOURCE_DIR}/include the header resolves through the plain -I above
|
||||||
-g
|
# and stays a normal header, so the sources include it as <khash.h>.
|
||||||
|
target_include_directories(node SYSTEM PRIVATE
|
||||||
|
${PROJECT_SOURCE_DIR}/include/khash
|
||||||
)
|
)
|
||||||
|
target_compile_options(node PRIVATE
|
||||||
|
"${SKALACOIN_C_WARNINGS}"
|
||||||
|
"${SKALACOIN_INSTRUMENT_COMPILE}"
|
||||||
|
"$<${SKALACOIN_IS_DEBUGLIKE}:${SKALACOIN_C_FLAGS_DEBUGLIKE}>"
|
||||||
|
"$<$<NOT:${SKALACOIN_IS_DEBUGLIKE}>:${SKALACOIN_C_FLAGS_OPTIMIZED}>"
|
||||||
|
# The static analyzer runs on our C sources only, never on the vendored
|
||||||
|
# C++ below, and never alongside the sanitizers.
|
||||||
|
"$<$<CONFIG:Analyzer>:${SKALACOIN_ANALYZER_FLAGS}>"
|
||||||
|
# Strict is Debug with the warning set turned into a build gate.
|
||||||
|
"$<$<CONFIG:Strict>:${SKALACOIN_WERROR_FLAG}>"
|
||||||
|
)
|
||||||
|
target_link_options(node PRIVATE "${SKALACOIN_INSTRUMENT_LINK}")
|
||||||
|
|
||||||
|
if(SKALACOIN_ENABLE_LTO AND SKALACOIN_IPO_SUPPORTED)
|
||||||
|
set_target_properties(node PROPERTIES
|
||||||
|
INTERPROCEDURAL_OPTIMIZATION_RELEASE ON
|
||||||
|
INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO ON
|
||||||
|
INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL ON
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_compile_definitions(node PRIVATE
|
target_compile_definitions(node PRIVATE
|
||||||
CHAIN_DATA_DIR="${CMAKE_BINARY_DIR}/chain_data"
|
CHAIN_DATA_DIR="${CMAKE_BINARY_DIR}/chain_data"
|
||||||
$<$<BOOL:${SKALACOIN_AUTOLYKOS2_REF_AVAILABLE}>:SKALACOIN_AUTOLYKOS2_REF_AVAILABLE>
|
$<$<BOOL:${SKALACOIN_AUTOLYKOS2_REF_AVAILABLE}>:SKALACOIN_AUTOLYKOS2_REF_AVAILABLE>
|
||||||
@@ -235,3 +588,18 @@ target_compile_definitions(node PRIVATE
|
|||||||
$<$<BOOL:1>:_DEFAULT_SOURCE>
|
$<$<BOOL:1>:_DEFAULT_SOURCE>
|
||||||
)
|
)
|
||||||
set_target_properties(node PROPERTIES OUTPUT_NAME "skalacoin_node")
|
set_target_properties(node PROPERTIES OUTPUT_NAME "skalacoin_node")
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# Configuration summary
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
message(STATUS "skalacoin: build type ${CMAKE_BUILD_TYPE}")
|
||||||
|
message(STATUS "skalacoin: compiler ${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION}")
|
||||||
|
message(STATUS "skalacoin: warnings-as-err ${SKALACOIN_WERROR} (always on in Strict)")
|
||||||
|
message(STATUS "skalacoin: sanitizers [Debug/Strict] ${SKALACOIN_SANITIZER_FLAGS}")
|
||||||
|
message(STATUS "skalacoin: static analyzer [Analyzer] ${SKALACOIN_ANALYZER_FLAGS}")
|
||||||
|
message(STATUS "skalacoin: hardening ${SKALACOIN_ENABLE_HARDENING}")
|
||||||
|
message(STATUS "skalacoin: LTO ${SKALACOIN_ENABLE_LTO}")
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Analyzer" AND NOT SKALACOIN_ANALYZER_FLAGS)
|
||||||
|
message(STATUS "skalacoin: NOTE - Analyzer config has no static analyzer on "
|
||||||
|
"${CMAKE_C_COMPILER_ID}; use scan-build over this build tree")
|
||||||
|
endif()
|
||||||
|
|||||||
+11
-1
@@ -6,7 +6,7 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <khash/khash.h>
|
#include <khash.h>
|
||||||
#include <crypto/crypto.h>
|
#include <crypto/crypto.h>
|
||||||
#include <block/transaction.h>
|
#include <block/transaction.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -33,7 +33,17 @@ typedef struct {
|
|||||||
// TODO: Additional things
|
// TODO: Additional things
|
||||||
} balance_sheet_entry_t;
|
} balance_sheet_entry_t;
|
||||||
|
|
||||||
|
// KHASH_INIT expands to khash's own implementation, which is not -Wconversion
|
||||||
|
// clean. -isystem silences the header itself but not code expanded from its
|
||||||
|
// macros, because the diagnostic is attributed to this line.
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wconversion"
|
||||||
|
#endif
|
||||||
KHASH_INIT(balance_sheet_map_m, key32_t, balance_sheet_entry_t, 1, hash_key32, eq_key32)
|
KHASH_INIT(balance_sheet_map_m, key32_t, balance_sheet_entry_t, 1, hash_key32, eq_key32)
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
extern khash_t(balance_sheet_map_m)* sheetMap;
|
extern khash_t(balance_sheet_map_m)* sheetMap;
|
||||||
|
|
||||||
void BalanceSheet_Init();
|
void BalanceSheet_Init();
|
||||||
|
|||||||
@@ -113,7 +113,6 @@ int main() {
|
|||||||
* Added destructor
|
* Added destructor
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#ifndef __AC_KHASH_H
|
#ifndef __AC_KHASH_H
|
||||||
#define __AC_KHASH_H
|
#define __AC_KHASH_H
|
||||||
|
|
||||||
|
|||||||
+10
-1
@@ -2,11 +2,20 @@
|
|||||||
#define TXMEMPOOL_H
|
#define TXMEMPOOL_H
|
||||||
|
|
||||||
#include <block/transaction.h>
|
#include <block/transaction.h>
|
||||||
#include <khash/khash.h>
|
#include <khash.h>
|
||||||
#include <utils.h>
|
#include <utils.h>
|
||||||
#include <uint256.h>
|
#include <uint256.h>
|
||||||
|
|
||||||
|
// See balance_sheet.h: khash's macro expansion is not -Wconversion clean, and
|
||||||
|
// -isystem does not cover code expanded from a system header's macros.
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wconversion"
|
||||||
|
#endif
|
||||||
KHASH_INIT(tx_mempool_map_m, key32_t, signed_transaction_t, 1, hash_key32, eq_key32)
|
KHASH_INIT(tx_mempool_map_m, key32_t, signed_transaction_t, 1, hash_key32, eq_key32)
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
extern khash_t(tx_mempool_map_m)* txMempool;
|
extern khash_t(tx_mempool_map_m)* txMempool;
|
||||||
|
|
||||||
void TxMempool_Init();
|
void TxMempool_Init();
|
||||||
|
|||||||
+12
-4
@@ -767,7 +767,7 @@ static bool VerifyChainFully(blockchain_t* chain) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use when error
|
// Use when error
|
||||||
void KillEverythingAndExit(net_node_t* node, blockchain_t* chain) {
|
[[noreturn]] void KillEverythingAndExit(net_node_t* node, blockchain_t* chain) {
|
||||||
Node_Destroy(node);
|
Node_Destroy(node);
|
||||||
currentChain = NULL;
|
currentChain = NULL;
|
||||||
Chain_Destroy(chain);
|
Chain_Destroy(chain);
|
||||||
@@ -918,18 +918,21 @@ int main(int argc, char* argv[]) {
|
|||||||
if (read != 32) {
|
if (read != 32) {
|
||||||
fprintf(stderr, "failed to read wallet file\n");
|
fprintf(stderr, "failed to read wallet file\n");
|
||||||
fclose(walletFile);
|
fclose(walletFile);
|
||||||
|
KillEverythingAndExit(node, chain);
|
||||||
}
|
}
|
||||||
|
|
||||||
read = fread(minerCompressedPubkey, 1, 33, walletFile);
|
read = fread(minerCompressedPubkey, 1, 33, walletFile);
|
||||||
if (read != 33) {
|
if (read != 33) {
|
||||||
fprintf(stderr, "failed to read wallet file\n");
|
fprintf(stderr, "failed to read wallet file\n");
|
||||||
fclose(walletFile);
|
fclose(walletFile);
|
||||||
|
KillEverythingAndExit(node, chain);
|
||||||
}
|
}
|
||||||
|
|
||||||
read = fread(minerAddress, 1, 32, walletFile);
|
read = fread(minerAddress, 1, 32, walletFile);
|
||||||
if (read != 32) {
|
if (read != 32) {
|
||||||
fprintf(stderr, "failed to read wallet file\n");
|
fprintf(stderr, "failed to read wallet file\n");
|
||||||
fclose(walletFile);
|
fclose(walletFile);
|
||||||
|
KillEverythingAndExit(node, chain);
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(walletFile);
|
fclose(walletFile);
|
||||||
@@ -1387,9 +1390,14 @@ int main(int argc, char* argv[]) {
|
|||||||
uint64_t nextReq = start;
|
uint64_t nextReq = start;
|
||||||
|
|
||||||
const int maxInFlight = MAX_PARALLEL_FETCHES;
|
const int maxInFlight = MAX_PARALLEL_FETCHES;
|
||||||
uint64_t requestedHeights[64];
|
// Zeroed so a slot is never read before it is written. Slots below
|
||||||
int retryCount[64];
|
// inFlight are always initialized by the fill loop, but that is a
|
||||||
uint64_t sentAtMs[64];
|
// loop invariant -fanalyzer cannot prove, and an explicit
|
||||||
|
// initializer is cheaper than teaching it (one memset per sync
|
||||||
|
// command) and survives future changes to the fill logic.
|
||||||
|
uint64_t requestedHeights[64] = {0};
|
||||||
|
int retryCount[64] = {0};
|
||||||
|
uint64_t sentAtMs[64] = {0};
|
||||||
int inFlight = 0;
|
int inFlight = 0;
|
||||||
|
|
||||||
if (maxInFlight > (int)(sizeof(requestedHeights)/sizeof(requestedHeights[0]))) {
|
if (maxInFlight > (int)(sizeof(requestedHeights)/sizeof(requestedHeights[0]))) {
|
||||||
|
|||||||
@@ -93,6 +93,15 @@ static void* TcpServer_clientthreadprocess(void* ptr) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// listenFd is borrowed, not owned: it is ptr->sockFd / ptr->sockFdV4, handed
|
||||||
|
// over by TcpServer_Start and closed by TcpServer_Stop. GCC's -fanalyzer infers
|
||||||
|
// from accept() that the fd is open and then holds this function responsible
|
||||||
|
// for closing it, so it reports a leak on every path that leaves the loop.
|
||||||
|
// Clang does not know this warning group, hence the guard.
|
||||||
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wanalyzer-fd-leak"
|
||||||
|
#endif
|
||||||
static void* TcpServer_threadprocess(void* ptr) {
|
static void* TcpServer_threadprocess(void* ptr) {
|
||||||
tcpaccept_thread_args_t* args = (tcpaccept_thread_args_t*)ptr;
|
tcpaccept_thread_args_t* args = (tcpaccept_thread_args_t*)ptr;
|
||||||
if (!args || !args->serverPtr) {
|
if (!args || !args->serverPtr) {
|
||||||
@@ -188,6 +197,9 @@ static void* TcpServer_threadprocess(void* ptr) {
|
|||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
tcp_server_t* TcpServer_Create() {
|
tcp_server_t* TcpServer_Create() {
|
||||||
tcp_server_t* svr = (tcp_server_t*)malloc(sizeof(*svr));
|
tcp_server_t* svr = (tcp_server_t*)malloc(sizeof(*svr));
|
||||||
@@ -226,6 +238,16 @@ void TcpServer_Destroy(tcp_server_t* ptr) {
|
|||||||
free(ptr);
|
free(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Both sockets are handed to the caller through ptr->sockFd / ptr->sockFdV4 and
|
||||||
|
// closed by TcpServer_Stop, so neither leaks. -fanalyzer loses track of the
|
||||||
|
// first store across the second socket's branches and reports it anyway; the
|
||||||
|
// report is positional, not semantic — swapping the IPv6 and IPv4 blocks moves
|
||||||
|
// the warning from fd6 to fd4, and deleting the unrelated second block silences
|
||||||
|
// it entirely.
|
||||||
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wanalyzer-fd-leak"
|
||||||
|
#endif
|
||||||
void TcpServer_Init(tcp_server_t* ptr, unsigned short port, const char* addr) {
|
void TcpServer_Init(tcp_server_t* ptr, unsigned short port, const char* addr) {
|
||||||
if (!ptr || !addr) {
|
if (!ptr || !addr) {
|
||||||
return;
|
return;
|
||||||
@@ -273,7 +295,18 @@ void TcpServer_Init(tcp_server_t* ptr, unsigned short port, const char* addr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Same borrowed-fd false positive as TcpServer_threadprocess: listen() teaches
|
||||||
|
// -fanalyzer that ptr->sockFd / ptr->sockFdV4 are open passive sockets, so it
|
||||||
|
// expects this function to close them. They belong to the tcp_server_t and are
|
||||||
|
// closed by TcpServer_Stop.
|
||||||
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wanalyzer-fd-leak"
|
||||||
|
#endif
|
||||||
void TcpServer_Start(tcp_server_t* ptr, int maxcons) {
|
void TcpServer_Start(tcp_server_t* ptr, int maxcons) {
|
||||||
if (!ptr || (ptr->sockFd < 0 && ptr->sockFdV4 < 0) || maxcons <= 0 || ptr->isRunning) {
|
if (!ptr || (ptr->sockFd < 0 && ptr->sockFdV4 < 0) || maxcons <= 0 || ptr->isRunning) {
|
||||||
return;
|
return;
|
||||||
@@ -347,6 +380,9 @@ void TcpServer_Start(tcp_server_t* ptr, int maxcons) {
|
|||||||
pthread_mutex_unlock(&ptr->clientsMutex);
|
pthread_mutex_unlock(&ptr->clientsMutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
void TcpServer_Stop(tcp_server_t* ptr) {
|
void TcpServer_Stop(tcp_server_t* ptr) {
|
||||||
if (!ptr || !ptr->isRunning) {
|
if (!ptr || !ptr->isRunning) {
|
||||||
|
|||||||
@@ -181,6 +181,14 @@ static void* UdpNode_RetryThreadProc(void* arg) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Same borrowed/escaped-fd false positive as TcpServer_Init: both sockets are
|
||||||
|
// handed to the caller through node->sockFd / node->sockFdV4 and closed by
|
||||||
|
// UdpNode_Stop. -fanalyzer loses the first store across the second socket's
|
||||||
|
// branches and reports it as a leak.
|
||||||
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wanalyzer-fd-leak"
|
||||||
|
#endif
|
||||||
int UdpNode_Init(udp_node_t* node, uint16_t port) {
|
int UdpNode_Init(udp_node_t* node, uint16_t port) {
|
||||||
if (!node) {
|
if (!node) {
|
||||||
return -1;
|
return -1;
|
||||||
@@ -237,6 +245,9 @@ int UdpNode_Init(udp_node_t* node, uint16_t port) {
|
|||||||
pthread_mutex_init(&node->pingsMutex, NULL);
|
pthread_mutex_init(&node->pingsMutex, NULL);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
void UdpNode_SetCallbacks(udp_node_t* node,
|
void UdpNode_SetCallbacks(udp_node_t* node,
|
||||||
void (*on_pong)(udp_node_t*, const struct sockaddr_storage*, uint64_t, int, uint64_t, void*),
|
void (*on_pong)(udp_node_t*, const struct sockaddr_storage*, uint64_t, int, uint64_t, void*),
|
||||||
|
|||||||
Reference in New Issue
Block a user