Repository navigation
fix(snippet): return one candidate per snippet region in multi-component scans - #123
Merged
Merged
Conversation
…ent scans A file concatenating code from several components collapsed to a single candidate and segfaulted with -M >= 2. Three issues, same selection path shared by -M/--max-snippets and -S/--snippet-scan (the /scan/snippets endpoint): 1. match_data_copy() dropped scan_ower (and lines_matched). The multi-snippet output path copies matches, so print_json_match() dereferenced a NULL scan_ower -> SIGSEGV (src/report.c:337) on any -M >= 2. 2. biggest_snippet() created a per-region list but added every candidate to matches_list_array[i] (almost always index 0), so all blocks competed in a single list and only one component survived. Reworked to place each match in the list of the region whose ranges intersect it, opening a new list per non-overlapping region up to MAX_MULTIPLE_COMPONENTS. 3. snippet-scan (-S) defaulted to SCAN_MAX_SNIPPETS_DEFAULT (1), collapsing the discovery endpoint to a single group regardless of the input. It now opens up to MAX_MULTIPLE_COMPONENTS groups unless the caller explicitly raised -M. Also order multi-snippet matches by covered lines (more_lines_matched) so the most-relevant block is the principal match (match_index 0). Verified against KB ossv2 on the concatenated nginx/tomcat/curl/httpd/postgres fixture: -M 9 returns curl+httpd (tomcat too with --ignore-file-ext), main.java -> tomcat only, main.py -> no match, and -S returns one group per region. No segfault across -M 1..9; valgrind clean. nginx/postgres remain out of reach (popular-hash blocks dropped by the matchmap category cap) and are tracked as pending in the regression test. Adds tests/snippet_multicomponent/ regression test (skips without a KB). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bump SCANOSS_VERSION 6.0.1-crc64 -> 6.0.2-crc64. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Problem
A file that concatenates code from multiple components returned a single candidate and segfaulted with
-M ≥ 2. This affects both-M/--max-snippetsand-S/--snippet-scan(the/scan/snippetsendpoint path), which share the selection logic.Reproduced locally against the
ossv2KB with the WFP ofmain.c(nginx/tomcat/curl/httpd/postgres concatenation):scanoss -M 1→ a single candidate (curl).scanoss -M 9→ segfault (report.c:337,scan_ower == NULL).scanoss -S -→ a singlesnippet_groupwith 29 candidates (all tomcat).Diagnosis and fix
1. Segfault with
-M ≥ 2·src/match.cmatch_data_copy()did not copyscan_ower(norlines_matched). The multi-snippet path copies the matches, soprint_json_match()dereferenced a nullscan_ower. Both fields are now copied.2. Collapse into a single block ·
src/snippet_selection.cbiggest_snippet()created one list per region but appended every candidate tomatches_list_array[i](almost always index0), so all of them competed in a single list and only one component survived. Rewritten: each match goes to the list of the region whose ranges it intersects; if none intersects and there is room, it opens a new list for a non-overlapping region, up toMAX_MULTIPLE_COMPONENTS.3.
-S/--snippet-scancollapsed by default ·src/purl_scan.cThe discovery endpoint used
SCAN_MAX_SNIPPETS_DEFAULT(= 1), collapsing the output into a single group regardless of the input (the API calls-Swithout-M). It now opens up toMAX_MULTIPLE_COMPONENTSgroups unless the caller explicitly raises-M.Ordering by relevance ·
src/match.cNew
more_lines_matchedcomparator so the block with the most covered lines becomes the main match (match_index 0).Verified results (KB
ossv2)-M 9 main.c-M 9 --ignore-file-ext main.c-M 9 main.java-M 9 main.py-S -(without-M)No segfault in
-M 1..9; clean valgrind in-M 9and-S.Pending (not included, point 3 of the issue)
nginx (100-line block) and postgres (10 lines) still do not appear. Cause:
biggest_snippet()only considers the file with the most hits per sector, and before that the matchmap prioritizes low-popularity hashes and cuts off atMAX_MATCHMAP_FILES. The nginx block (auto/module, build boilerplate) is made of very popular hashes (each one in 900-3600 OSS files) that fall outside the cutoff, so the file never enters the matchmap with enough hits. This is a recall-vs-performance tradeoff; it is marked asPENDINGin the test.Test
tests/snippet_multicomponent/— validates the 4-Mcases, the ordering by relevance, and the-Soutput (≥3 groups without passing-M). It skips gracefully if no KB/binary is available, so it does not break CI that builds without a KB.🤖 Generated with Claude Code