Skip to content

No sanitizer leg builds the bank example, so the UB in #663 was unreachable by every ASan/UBSan job #679

Description

@Yaraslaut

The gap

Nothing in the bank example is ever built with a sanitizer. Not the GUI, not bank_lib, not bank_tests. So #663 — a double → std::int64_t conversion that UBSan diagnoses in one line, on the path of every amount typed into the bank GUI — could not have been caught by any leg, however many times it ran.

examples/bank/CMakeLists.txt is not the problem: it calls apply_sanitizers(ladder_bank_server ${AF_SANITIZER}) when the variable is defined, and examples/bank/gui/CMakeLists.txt follows the same pattern. The option is simply never on in a sanitizer configure.

Verified by reading the three configures

Grep for the flag across every workflow, on 563502ab:

$ grep -n "MORPH_BUILD_BANK" .github/workflows/*.yml
.github/workflows/wasm-demo.yml:97:  -DMORPH_BUILD_EXAMPLES=ON -DMORPH_BUILD_BANK_EXAMPLE=ON \
.github/workflows/wasm-demo.yml:98:  -DMORPH_BUILD_BANK_GUI=ON -DMORPH_BUILD_TESTS=OFF
.github/workflows/ci.yml:1240:            -DMORPH_BUILD_BANK_EXAMPLE=ON \      # ladder scenarios (no sanitizer)
.github/workflows/ci.yml:1854:            -DMORPH_BUILD_BANK_EXAMPLE=ON \      # linux-all-features (no sanitizer)
.github/workflows/ci.yml:1855:            -DMORPH_BUILD_BANK_GUI=ON \
.github/workflows/ci.yml:2319:            -DMORPH_BUILD_BANK_EXAMPLE=ON \      # clang-tidy (configure-only, never built)
.github/workflows/ci.yml:2320:            -DMORPH_BUILD_BANK_GUI=ON \

None of those five sites is a sanitizer leg, and the two jobs that are:

  • linux-sanitizers (ci.yml:388), matrix [clang-asan, clang-tsan, clang-ubsan], configures -DMORPH_BUILD_NET=ON -DMORPH_BUILD_OFFLINE_SQLITE=ON and nothing else. No bank, no ladder.
  • ladder-sanitizers (ci.yml:1446), configures clang-asan with -DMORPH_BUILD_QT=ON -DMORPH_BUILD_LADDER=ON -DMORPH_LADDER_RUNGS=all. Bank is deliberately not a rung — it is absent from examples/rungs.txt and never calls morph_add_rung() (see examples/bank/CMakeLists.txt's own long comment on why) — so MORPH_BUILD_LADDER=ON does not reach it.

apply_sanitizers is -fsanitize=address,undefined for the asan mode (cmake/compiler_options.cmake), so an ASan leg that included bank would carry UBSan with it and would have flagged #663 on the first amount any test parsed.

What it would have cost

Measured locally on a 12-core box, clang 22.1.8, Qt 6.11.2, cold build directory: cmake --build build/clang-debug --target bank_gui_tests from a configured tree is 63s wall / 334s CPU, 80 ninja edges, of which the vendored Lightweight ORM is the bulk. The configure that produces it is 70s. So this is a real cost on a hosted runner, not a free addition — which is presumably why it was never on, and is the thing a fix has to weigh rather than assume.

That leaves at least three shapes, and choosing between them is the work:

  1. add -DMORPH_BUILD_BANK_EXAMPLE=ON -DMORPH_BUILD_BANK_GUI=ON to linux-sanitizers' clang-ubsan leg only — UBSan is what catches this class, and it avoids paying the ORM build three times;
  2. a separate bank-sanitizers job, mirroring kanban-tsan's precedent of splitting one expensive suite out of the matrix;
  3. decide bank is out of sanitizer scope deliberately and write that down, the way examples/bank/CMakeLists.txt already writes down why bank is not a rung.

Verification status

  • Reproduced: the grep above and the two sanitizer jobs' configure flags, read from ci.yml on 563502ab. The build cost is a real local measurement, not an estimate.
  • Reproduced: that UBSan diagnoses bank GUI: parseMinor casts an unbounded double to int64_t; 1e30 in any amount field is UB #663 when bank is instrumented — a hand-built -fsanitize=undefined -fno-sanitize-recover=undefined binary over the pre-fix header gives runtime error: 1e+32 is outside the range of representable values of type 'long'.
  • Not verified: whether turning bank on in a sanitizer leg surfaces other pre-existing findings. It very likely does — bank_lib has never been instrumented — and that backlog is the real cost of option 1 or 2. I did not build it under a sanitizer to find out, and whoever takes this should measure that before committing to a shape.
  • Not verified: whether the ORM itself is sanitizer-clean. bank_lib deliberately skips apply_warnings() because the vendored ORM headers are not -Werror clean; whether the same is true of its runtime behaviour under ASan/UBSan is unknown.

What would change the verdict

Close as invalid if a leg does instrument bank and I misread its flags. Close as wontfix if the considered answer is option 3 — but then examples/bank/CMakeLists.txt should say so beside the apply_sanitizers call it currently makes, because that call reads today as though it were reachable from CI and it is not.

Raised by #663's filer and by its triage ("Worth its own ticket if it is not already covered; I have not checked whether the sanitizer legs' option set is deliberate or incidental"). Checked here: it is incidental, not written down anywhere. Found while working #663 (PR #676). Filed, not folded.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VptDWG2fKr2vBnLSJcgzgW

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: ciSubsystem: ciarea: ladderSubsystem: ladderbugSomething isn't workingtriage: validWell-framed; implement as written

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions