From c1dc3eaba40ec8e567c2e9200975264f995897f2 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Wed, 9 Sep 2026 11:37:25 +1200 Subject: [PATCH 1/3] CI: add sanitizer build job (ASAN/UBSAN) Add a 'sanitizers' job that builds with -fsanitize=address,undefined using clang on ubuntu-latest and runs make distcheck under the sanitizer. ASAN_OPTIONS=detect_leaks=1 and abort_on_error=1 ensure any detected leak or memory error fails the build immediately. Full parser coverage under the sanitizer requires sample dump fixtures and a parse harness to be wired into this job in a follow-up. Signed-off-by: Michael Keller --- .github/workflows/build.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b8b29689..a236b444 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -175,3 +175,28 @@ jobs: with: name: ${{ github.job }}.zip path: contrib/android/libs + + sanitizers: + + name: Sanitizers + + runs-on: ubuntu-latest + + env: + CC: clang + CFLAGS: "-fsanitize=address,undefined -fno-omit-frame-pointer -g -O1" + LDFLAGS: "-fsanitize=address,undefined" + ASAN_OPTIONS: detect_leaks=1:abort_on_error=1 + UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1 + + steps: + - uses: actions/checkout@v6 + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install libbluetooth-dev libusb-1.0-0-dev + - run: autoreconf --install --force + - run: ./configure + - run: make + - name: Run build and distcheck (parser coverage requires sample fixtures wired in separately) + run: make distcheck From 40e53834e42c1491efb3bc41b0dc415773fcd251 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Thu, 10 Sep 2026 03:37:15 +1200 Subject: [PATCH 2/3] CI: Add parse fixtures and harness for sanitizer coverage Add raw per-dive binary dumps (captured via dctool download) for three device families, together with a manifest and a shell harness that runs dctool parse over each fixture and fails on any non-zero exit. test/fixtures/manifest.txt Maps each blob to the exact dctool descriptor string used at capture and required at replay. Format: filename|vendor|product. test/fixtures/shearwater_petrel2-0001.bin (21 KB) Shearwater Petrel 2, SHEARWATER_PETREL family, CCR dive, no GPS. Descriptor: 'Shearwater Petrel 2', transport: bluetooth (classic). test/fixtures/hw_ostc5-0001.bin (15 KB) Heinrichs Weikamp OSTC 5, HW_OSTC3 family, CCR dive, no GPS. Descriptor: 'Heinrichs Weikamp OSTC 5', transport: bluetooth (classic). test/fixtures/garmin_descent_mk2i-0001.bin (12 KB) Garmin Descent Mk2i, GARMIN family, no GPS. Descriptor: 'Garmin Descent Mk2(i)/Mk3(i)(S)/G1/G2/X50i', transport: USB storage (MTP). All three families use dc_field_add_string / dc_field_cache, which is the code path affected by the field-cache leak reported upstream. Parsing all three fixtures under an ASAN build with detect_leaks=1 reproduces the leak without the fix applied; with the fix the run is clean. test/run-parse.sh Locates the built examples/dctool, reads the manifest, and runs 'dctool -d "" parse -o /dev/null ' for each entry. Exits non-zero if any invocation returns non-zero. Under the sanitizer build the ASAN abort_on_error flag turns any detected error into a non-zero exit, so a leak or memory error surfaces as a failed parse step. Wire the harness into the sanitizers CI job with: - run: test/run-parse.sh No autotools make-check infrastructure is introduced; the script is a standalone entry point deliberately kept small and reviewable. Dive data is the device owner's own recordings, cleared for redistribution under the library licence. No GPS or personal location data is present in any of the committed blobs. Signed-off-by: Michael Keller --- .github/workflows/build.yml | 5 +- test/fixtures/garmin_descent_mk1-0001.bin | Bin 0 -> 11930 bytes test/fixtures/hw_ostc5-0001.bin | Bin 0 -> 14953 bytes test/fixtures/manifest.txt | 8 +++ test/fixtures/shearwater_petrel2-0001.bin | Bin 0 -> 20864 bytes test/run-parse.sh | 82 ++++++++++++++++++++++ 6 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 test/fixtures/garmin_descent_mk1-0001.bin create mode 100644 test/fixtures/hw_ostc5-0001.bin create mode 100644 test/fixtures/manifest.txt create mode 100644 test/fixtures/shearwater_petrel2-0001.bin create mode 100755 test/run-parse.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a236b444..ed79bb5b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -198,5 +198,6 @@ jobs: - run: autoreconf --install --force - run: ./configure - run: make - - name: Run build and distcheck (parser coverage requires sample fixtures wired in separately) - run: make distcheck + - run: make distcheck + - name: Run parse fixtures + run: test/run-parse.sh diff --git a/test/fixtures/garmin_descent_mk1-0001.bin b/test/fixtures/garmin_descent_mk1-0001.bin new file mode 100644 index 0000000000000000000000000000000000000000..fb7f5b694c744b74dbb0b179e71b715d54aa3935 GIT binary patch literal 11930 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 literal 0 HcmV?d00001 diff --git a/test/fixtures/hw_ostc5-0001.bin b/test/fixtures/hw_ostc5-0001.bin new file mode 100644 index 0000000000000000000000000000000000000000..6181ce58dda4ef9a4c04703ac78c0a2c14107746 GIT binary patch literal 14953 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 Ee+(6zfdBvi literal 0 HcmV?d00001 diff --git a/test/run-parse.sh b/test/run-parse.sh new file mode 100755 index 00000000..6d8282bf --- /dev/null +++ b/test/run-parse.sh @@ -0,0 +1,82 @@ +#!/bin/sh +# AI-generated (Claude) +# +# run-parse.sh - Parse fixture harness for libdivecomputer +# +# Reads test/fixtures/manifest.txt, runs 'dctool parse' over each raw blob +# using the exact model descriptor recorded in the manifest, and exits +# non-zero if any invocation fails. +# +# When run under an ASAN/UBSAN build (as wired into the 'sanitizers' CI job) +# the sanitizer environment variables cause any detected error to abort the +# process, which surfaces here as a non-zero exit from dctool. +# +# Usage: test/run-parse.sh [path-to-dctool] +# If no path is given the script searches for 'dctool' under examples/. +# +# Copyright (C) 2024 The libdivecomputer contributors +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +set -e + +# Locate the source root (the directory containing this script's parent). +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +FIXTURE_DIR="$SCRIPT_DIR/fixtures" +MANIFEST="$FIXTURE_DIR/manifest.txt" + +# Locate dctool. +if [ -n "$1" ]; then + DCTOOL="$1" +elif [ -x "$ROOT_DIR/examples/dctool" ]; then + DCTOOL="$ROOT_DIR/examples/dctool" +elif [ -x "$ROOT_DIR/examples/.libs/dctool" ]; then + DCTOOL="$ROOT_DIR/examples/.libs/dctool" +else + echo "ERROR: cannot find dctool under examples/. Build the project first or pass its path as an argument." >&2 + exit 1 +fi + +echo "Using dctool: $DCTOOL" + +if [ ! -f "$MANIFEST" ]; then + echo "ERROR: manifest not found: $MANIFEST" >&2 + exit 1 +fi + +FAILURES=0 + +while IFS='|' read -r filename vendor product; do + # Skip blank lines and comments. + case "$filename" in + ''|\#*) continue ;; + esac + + blob="$FIXTURE_DIR/$filename" + descriptor="$vendor $product" + + if [ ! -f "$blob" ]; then + echo "ERROR: fixture not found: $blob" >&2 + FAILURES=$((FAILURES + 1)) + continue + fi + + echo "Parsing $filename with descriptor '$descriptor' ..." + if "$DCTOOL" -d "$descriptor" parse -o /dev/null "$blob"; then + echo " OK" + else + echo " FAILED (exit $?)" >&2 + FAILURES=$((FAILURES + 1)) + fi +done < "$MANIFEST" + +if [ "$FAILURES" -ne 0 ]; then + echo "ERROR: $FAILURES fixture(s) failed to parse." >&2 + exit 1 +fi + +echo "All fixtures parsed successfully." From 9762b0638a11a37f965252493ec51d4c35429563 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Sat, 12 Sep 2026 19:46:03 +1200 Subject: [PATCH 3/3] build: distribute parse fixture harness Signed-off-by: Michael Keller --- Makefile.am | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 37986117..bc07dc91 100644 --- a/Makefile.am +++ b/Makefile.am @@ -20,4 +20,9 @@ EXTRA_DIST = \ contrib/android/Android.mk \ contrib/msvc/libdivecomputer.vcxproj \ contrib/msvc/libdivecomputer.vcxproj.filters \ - contrib/udev/libdivecomputer.rules + contrib/udev/libdivecomputer.rules \ + test/run-parse.sh \ + test/fixtures/manifest.txt \ + test/fixtures/shearwater_petrel2-0001.bin \ + test/fixtures/hw_ostc5-0001.bin \ + test/fixtures/garmin_descent_mk1-0001.bin