ci: clang-cl sanitizer/static-analysis job + Windows-ASan + cffi-ASan + blocking clang-tidy (LEO-327) (#40)
* fix(c): size sw_decode_field for the local UTF-8 worst case (LEO-327) Surfaced by the new blocking clang-tidy gate (clang-analyzer security.ArrayBound): the 3-bytes-per-input-byte allocation bound was only safe because sw_cp1252_codepoint can never return above U+FFFD -- a cross-function invariant the analyzer (rightly) refuses to assume. Size for the 4-byte UTF-8 worst case instead so the bound is provable from the function alone. Output bytes are unchanged (allocation size only); golden byte-compat suites pass untouched. Vendored mirror synced. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * build: teach CMake the clang-cl flag surface (LEO-327) Detect clang-cl once (compiler-id Clang + simulate-id MSVC) as SW_CLANG_CL and branch the flag blocks on it: warnings become /W4 /WX /guard:cf plus the clang-cl set the coding standard asks for (-Wconversion -Wshadow -Wformat=2; /sdl and /analyze are cl.exe-only and would trip -Wunknown-argument under /WX), and SW_ENABLE_ASAN becomes ASan via embedded /DEFAULTLIB directives plus UBSan in trap mode (no runtime to link; UB aborts and fails ctest). SW_BUILD_FUZZ now rejects clang-cl explicitly -- libFuzzer is not wired for the windows-msvc target here. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci: clang-cl, MSVC-ASan, cffi-ASan, and blocking clang-tidy legs (LEO-327) Four new native-CI legs: - native (windows, clang-cl): the coding standard's secondary compiler, VS bundled ClangCL toolset under Ninja in the existing Dev Shell pattern; plain /WX pass then ASan + UBSan-trap pass. - native (windows, msvc): second pass under MSVC AddressSanitizer -- the first CI leg to run the Windows-only test_session.c under a sanitizer. - cffi extension under ASan (ubuntu): extension built with -fsanitize=address, tests/test_native.py run under the preloaded runtime; catches FFI marshalling/lifetime bugs the pure-C runs cannot see. - clang-tidy (blocking) in native-linux via the exact-pinned PyPI wheel (22.1.8) and the new .clang-tidy check set: the repo's first blocking C static analysis. MSVC /analyze deliberately stays non-fatal (analyzer-version skew, see CMakeLists.txt). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * build: clang-cl passthrough for frame pointers + full UBSan runtime attempt (LEO-327) The plain clang-cl pass came back fully green (all /W4 /WX targets, all 5 tests incl. test_session), but the sanitizer pass tripped on -fno-omit-frame-pointer: not in clang-cl's accepted -f set, needs the /clang: passthrough spelling. Also switch UBSan from trap mode to the full diagnostic runtime (-fno-sanitize-recover=all) as the pre-agreed ladder attempt -- if the ubsan runtime does not link under link.exe/lld-link this commit gets reverted to trap mode. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci: pin RelWithDebInfo on the Windows sanitizer configures (LEO-327) Under clang-cl the configure resolved to a Debug configuration (/Od /RTC1 -MDd), and clang-cl refuses -fsanitize=address with the debug CRT. The CMakeLists RelWithDebInfo default only fires when the build type is truly unset, and CMake's Windows platform init supplies one for clang-cl. Pass -DCMAKE_BUILD_TYPE=RelWithDebInfo explicitly on the new Windows sanitizer configures, matching what native-linux's ASan pass already does for the same RTC/ASan reason. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci: static-lib-only clang-cl sanitizer pass (LEO-327) The RelWithDebInfo pin got the clang-cl ASan pass compiling; the link then failed on sensorwatch.dll only -- undefined __asan_* symbols. clang-cl's embedded ASan runtime /DEFAULTLIB directives cover executables but not a DLL linked directly by lld-link. The sanitizer pass exists to run the tests under ASan, and every test links sensorwatch_static, so build the pass with SW_BUILD_SHARED=OFF: identical TU coverage, no DLL link. The shipped DLL still gets a sanitizer build in the MSVC job's ASan pass (green). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * build: explicitly link the clang-cl ASan runtime; UBSan lands as trap mode (LEO-327) With SW_BUILD_SHARED=OFF the test executables failed the same way the DLL did -- undefined __asan_* at every image link. clang-cl's objects carry no usable ASan-runtime /DEFAULTLIB directives when CMake+Ninja invokes lld-link directly, so nothing links without naming the runtime. Resolve the /MD dynamic-runtime pair (clang_rt.asan_dynamic + _runtime_thunk) from the compiler's resource dir, covering both the legacy lib/windows and per-target lib layouts, and fail the configure loudly if absent. That also lets the DLL build again, so the SHARED=OFF stopgap is reverted, and the job now locates clang_rt.asan_dynamic-x86_64.dll for PATH before ctest. UBSan settles on trap mode per the pre-agreed ladder: the full ubsan_standalone runtime never linked cleanly in this direct-lld-link setup, and trap mode needs no runtime while still failing ctest on any UB. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: describe the CI that actually runs (LEO-327) Drops the 'clang-cl ... still aspirational' caveat from C_CODING_STANDARDS and replaces the 'run all three in CI' static-analysis claim with per-tool reality (clang-tidy blocking via pinned PyPI wheel; MSVC /analyze in CI non-fatal; cppcheck optional local). Documents the clang-cl flag set and trap-mode UBSan as wired, updates the README CI scope + build-option notes, the ROADMAP CI row, removes C_ABI's stale 'no fuzzing harness yet' line (LEO-414 shipped it), retargets the fuzz.yml header note now that LEO-327 landed per-PR, gitignores the new CI build dirs, and adds the CHANGELOG entry. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci: review round 1 -- real cache isolation + instrumentation assert for the ASan leg (LEO-327) Addresses the round-1 findings from the claude and codex reviewers: - python-native-asan now sets enable-cache: false explicitly. setup-uv's default is auto (= on for hosted runners) under the same key as the plain py3.13 test job, and uv's cache does not key on CFLAGS/LDFLAGS -- the codex reviewer reproduced a shared cache handing the leg an uninstrumented build. The comment that claimed no-cache is now true. - New assert step between build and pytest: the import of the extension WITHOUT the preloaded runtime must fail with the ASan came-first abort. A green run can no longer mean silently-uninstrumented. uv run --no-sync on both steps so nothing can rebuild the project without the ASan flags. Verified locally in both directions (instrumented -> correct abort; plain build -> import succeeds, assert would fail the job). - clang-cl leg throws when the ASan runtime DLL search misses, instead of letting ctest die with a bare 0xc0000135. - CMakeLists intro comment no longer claims /DEFAULTLIB directives resolve the sanitizer runtimes (contradicted the explicit-link workaround below). - Standards doc: the cffi-under-ASan run is a dedicated Linux job, not part of the Python test job. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci: review round 2 -- compile-time ASan symbol check + layout-validated runtime pair (LEO-327) Addresses the codex should-fix and both Copilot comments: - The instrumentation assert now checks the extension's dynamic symbols for compiler-emitted __asan_init/__asan_report_* references before the import-abort check. Codex reproduced the hole: -fsanitize=address in LDFLAGS alone links libasan and produces the identical came-first abort while every object stays uninstrumented, so the runtime-linkage check cannot catch a lost-CFLAGS build. Verified locally in all three directions: fully instrumented passes; the codex counterexample (LDFLAGS-only) and an ordinary build both fail at the symbol check. - The clang-cl ASan runtime discovery selects exactly one layout (per-target lib/<triple> first, then legacy lib/windows) and requires the complete asan_dynamic + runtime_thunk pair, instead of a glob where any single match configured successfully; a half-pair or mixed-layout result now FATAL_ERRORs at configure. The runtime-DLL search widens to both layout namings and excludes the thunk. - Standards doc grammar: 'the flag logic branches on SW_CLANG_CL'. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
L
lcj-claude-coder committed
9521730bf9878567e7a4ac9e48843c8aaecc05e2
Parent: 41dcef0
Committed by GitHub <noreply@github.com>
on 7/15/2026, 9:27:07 PM