Repository navigation
fix(pipeline): skip the function sort when the graph has no functions - #2083
Merged
Merged
Conversation
phase1_scan_functions() in pass_semantic_edges.c sorts the collected Function/Method node pointers so the func order is deterministic. When the graph has no Function or Method node at all -- a struct-only or config-only project -- node_ptrs is never allocated and the call becomes qsort(NULL, 0, ...). The func_count <= 0 early-out lives in phase 1b, which runs only after phase 1 has returned, so it never guards this call. A NULL qsort base is undefined behaviour. glibc declares the parameter nonnull, so UBSan on the Linux leg reports "null pointer passed as argument 1, which is declared to never be null" at the call. The macOS SDK carries no such attribute, so neither the default sanitizer build nor the Homebrew LLVM diag lane can flag it there. The existing pipeline_docstring_go_class test already reaches the call on Linux; the harness never gated on recoverable UBSan reports, so it went unnoticed. Guard the sort on func_count > 0. The new test pipeline_semantic_edges_no_functions indexes a Go struct-only fixture through the full pipeline and pins the fixture to what it claims (Struct >= 1, Function = Method = 0). On the Linux leg with UBSAN_OPTIONS=halt_on_error=1 the pipeline suite aborts at the qsort call before the fix and completes with it. Distilled from #1810 with co-author credit. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com> Co-authored-by: Yusuf S. Demirci <ysf-samet@outlook.com>
7 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Defect
phase1_scan_functions()insrc/pipeline/pass_semantic_edges.csorts the collected Function/Method node pointers (qsort(node_ptrs, (size_t)func_count, ...)) so the func order is deterministic. When the graph has no Function or Method node at all,node_ptrsis never allocated and the call isqsort(NULL, 0, ...). Thefunc_count <= 0early-out sits in phase 1b (phase1b_decode_and_build), which runs only after phase 1 has returned, so it never guards this call. A NULLqsortbase is undefined behaviour; glibc declares the parameternonnull, so UBSan reports it.Reachability
Any struct/class-only or config-only project indexed in full/moderate mode. The existing
pipeline_docstring_go_classtest (a Go struct-only fixture) already reaches the call on the Linux leg today.Which lane flags it
scripts/test.sh --suites pipeline CC=gcc CXX=g++ BUILD_DIR=build/linux-arm64) reportssrc/pipeline/pass_semantic_edges.c:996:5: runtime error: null pointer passed as argument 1, which is declared to never be nullwith the stackphase1_scan_functions<-cbm_pipeline_pass_semantic_edges<-predump_sem<-cbm_pipeline_run.qsort(usr/include/_stdlib.h) carries_Nonnullonly on the comparator, so both the default ASan/UBSan build and the Homebrew LLVM diag lane are silent. Verified with a stand-aloneqsort(NULL, 0, ...)probe under Apple clang 21 and Homebrew clang 22.1.8: no report from either.UBSAN_OPTIONS, no-fno-sanitize-recover, no report scan), so a recoverable report prints and the suite still passes -- which is why this went unnoticed. That is being recorded separately as a test-infra finding.Fix
Guard the sort on
func_count > 0(three lines, main's style). The re-derivation loop after it is already a no-op for an empty scan.Regression test
pipeline_semantic_edges_no_functions(suitepipeline) indexes a Go struct-only fixture (type Widget struct{}) through the full pipeline sopass_semantic_edgesruns, and pins the fixture to what it claims:Struct >= 1,Function = 0,Method = 0.Verification
origin/main(aa44c28), Linux container,UBSAN_OPTIONS=halt_on_error=1with the one unrelated pre-existing report site suppressed (the vendored objectscript_udl external scanner passing a NULL source tomemcpy, which surfaces asscanner.c:122andbits/string_fortified.h:29): thepipelinesuite aborts atpass_semantic_edges.c:996, exit 1. UBSan reports each site once per process andpipeline_docstring_go_classreaches it first in suite order; with the new test registered first for one diagnostic run, the abort is owned bypipeline_semantic_edges_no_functions.:996reports (only the two objectscript_udl reports remain);halt_on_error=1run completes, 275 passed, exit 0.build/c/test-runner pipeline extraction617 passed, zero UBSan output (before and after, as expected on this SDK).make -f Makefile.cbm lint-ciclean;bash scripts/check-no-test-skips.shOK; no rawfopen(, no SKIP/NOLINT, ASCII-only additions.Distilled from #1810 with co-author credit to @ysfsmet.
Refs #1810