Skip to content
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
43 changes: 39 additions & 4 deletions .github/workflows/sanitizers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ jobs:
label: ASan + UBSan
image: ghcr.io/aethersdr/aethersdr-ci:latest
build_type: Debug
# A Debug ASan tree with the core linked statically into ~250
# test executables outgrew the runner's disk ("No space left on
# device", three weeks running). One shared core keeps it near 17 GB.
shared_core: "ON"
# ON here too, not only under TSan: the churn test's documented
# failure class is a heap use-after-free on the receiver vector,
# and a sequential UAF produces no TSan report — catching it is
Expand Down Expand Up @@ -63,6 +67,7 @@ jobs:
# TSan cannot coexist with ASan, so build RelWithDebInfo and
# inject the TSan flags ourselves.
build_type: RelWithDebInfo
shared_core: "OFF"
receiver_churn: "ON"
extra_cxx: "-fsanitize=thread -g3 -fno-omit-frame-pointer -O1"
extra_ld: "-fsanitize=thread"
Expand Down Expand Up @@ -102,6 +107,7 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1

- name: Configure
id: configure
env:
# CFLAGS as well as CXXFLAGS. The tree is not all C++: WDSP and its
# Win32 port, liquid-dsp, rnnoise and the other vendored C libraries
Expand All @@ -122,10 +128,12 @@ jobs:
# that it is out of the default graph.
cmake -B build -G Ninja \
-DCMAKE_BUILD_TYPE=${{ matrix.sanitizer.build_type }} \
-DAETHER_SHARED_CORE=${{ matrix.sanitizer.shared_core }} \
Comment thread
ten9876 marked this conversation as resolved.
-DAETHER_ENABLE_HL2_RECEIVER_CHURN_TEST=${{ matrix.sanitizer.receiver_churn }} \
-DAETHER_ENABLE_HL2_TX_LOOPBACK_TEST=ON

- name: Build
id: build
# The SAME sanitizer flags as Configure, because this step also runs
# configure: vendored Opus is an ExternalProject_Add, so its child
# CMake project configures during the parent's BUILD, when a Configure-
Expand All @@ -140,7 +148,34 @@ jobs:
CFLAGS: ${{ matrix.sanitizer.extra_cxx }}
CXXFLAGS: ${{ matrix.sanitizer.extra_cxx }}
LDFLAGS: ${{ matrix.sanitizer.extra_ld }}
run: cmake --build build --parallel
run: |
# The after-build reading must survive a failed build, which is
# when it matters. Explicit status rather than an EXIT trap: this
# step runs under the container's sh, not bash.
df -h /
rc=0
cmake --build build --parallel || rc=$?
df -h /
exit "$rc"

# A Configure or Build failure skips every step below, ctest's report
# chain included, so the lane used to go dark with no artifact and no
# tracking issue (three weeks of "No space left on device"). This step
# leaves that chain a report, and a stable per-step fingerprint so the
# sticky issue collects repeats rather than opening one per week.
- name: Report a lane that did not build
id: nobuild
if: failure() && (steps.configure.outcome == 'failure' || steps.build.outcome == 'failure')
env:
FAILED_STEP: ${{ steps.configure.outcome == 'failure' && 'Configure' || 'Build' }}
run: |
{
echo "The lane did NOT run its tests: the $FAILED_STEP step failed."
echo "No result in this run is meaningful; the step log in the run says why."
} > report.txt
cp report.txt sanitizer.log
fp=$(printf 'nobuild:%s' "$FAILED_STEP" | sha256sum | cut -c1-12)
echo "fingerprint=${fp}" >> "$GITHUB_OUTPUT"

- name: Run tests under sanitizer
id: ctest
Expand Down Expand Up @@ -290,7 +325,7 @@ jobs:
echo "fingerprint=${fp}" >> "$GITHUB_OUTPUT"

- name: Upload sanitizer artifacts
if: steps.ctest.outputs.exit != '0'
if: (success() && steps.ctest.outputs.exit != '0') || (failure() && steps.nobuild.outcome == 'success')
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: ${{ matrix.sanitizer.id }}-${{ github.run_id }}
Expand All @@ -300,12 +335,12 @@ jobs:
retention-days: 30

- name: Open or update tracking issue
if: steps.ctest.outputs.exit != '0'
if: (success() && steps.ctest.outputs.exit != '0') || (failure() && steps.nobuild.outcome == 'success')
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3
env:
SAN_ID: ${{ matrix.sanitizer.id }}
SAN_LABEL: ${{ matrix.sanitizer.label }}
FP: ${{ steps.extract.outputs.fingerprint }}
FP: ${{ steps.extract.outputs.fingerprint || steps.nobuild.outputs.fingerprint }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
with:
script: |
Expand Down
19 changes: 19 additions & 0 deletions .github/workflows/static-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,25 @@ jobs:
fail=1
fi

# 5. AETHER_SHARED_CORE=ON (the ASan lane's link mode) must produce a
# shared core, and the default must stay static. The sanitizer
# lane is weekly, so this is the only per-PR check of the option.
# Asserted on the generated ninja file, like case 2.
if cmake -B /tmp/cfg-shared -S . -G Ninja \
-DAETHER_SHARED_CORE=ON > /tmp/shared.log 2>&1; then
grep -q '^build libaethercore\.so' /tmp/cfg-shared/build.ninja \
|| { echo "::error::AETHER_SHARED_CORE=ON configured, but no libaethercore.so rule"; fail=1; }
else
echo "::error::-DAETHER_SHARED_CORE=ON failed to configure"
tail -30 /tmp/shared.log
fail=1
fi
if [ -f /tmp/cfg-thread/build.ninja ] \
&& ! grep -q '^build libaethercore\.a' /tmp/cfg-thread/build.ninja; then
echo "::error::the default configure no longer builds a static libaethercore.a"
fail=1
fi

exit "$fail"

static-checks:
Expand Down
23 changes: 22 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,27 @@ if(NOT AETHERSDR_SANITIZER STREQUAL "none")
message(STATUS "AetherSDR: sanitizer = ${AETHERSDR_SANITIZER} (whole tree)")
unset(_aether_san_list)
endif()
# Build libaethercore as one shared library instead of a static archive. The
# ~250 test executables that link the core each absorb a full static copy
# (~200 MB in a Debug ASan build), which exhausts a hosted runner's disk;
# shared, they average ~13 MB. For the sanitizer lane only, never for releases:
# the shipped app links the core statically.
option(AETHER_SHARED_CORE "Build libaethercore as a shared library (sanitizer CI only)" OFF)
if(AETHER_SHARED_CORE)
if(WIN32)
message(FATAL_ERROR "AETHER_SHARED_CORE is not supported on Windows")
endif()
# Vendored static libraries end up inside the shared core. Their symbols
# do not reach the core's consumers: a PRIVATE dependency stops at a
# shared library, and aether_wdsp is built with hidden visibility. A test
# that calls WDSP directly while linking only aethercore links against
# the static core and fails here; go through core/dsp/WdspChannel.h.
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(_aether_core_library_type SHARED)
Comment thread
ten9876 marked this conversation as resolved.
message(STATUS "AetherSDR: libaethercore built SHARED (AETHER_SHARED_CORE)")
else()
set(_aether_core_library_type STATIC)
endif()
option(ENABLE_RTL "Enable the experimental receive-only RTL-SDR backend" ON)
# Which SSB modulator the Hermes-Lite 2 transmit path is BUILT with. There is
# no runtime switch and there must not be one: two silent transmit paths are
Expand Down Expand Up @@ -1574,7 +1595,7 @@ add_subdirectory(third_party/wdsp EXCLUDE_FROM_ALL)
# their feature blocks). Qt resources (resources.qrc, DFNR model qrc) stay on
# the executable: rcc registration runs from the exe's static initializers and
# is process-global, so core code reading ":/..." keeps working.
add_library(aethercore STATIC
add_library(aethercore ${_aether_core_library_type}
${CORE_SOURCES}
${MODEL_SOURCES}
${RNNOISE_SOURCES}
Expand Down
Loading