Parasoft C/C++test Standard CLI and SonarQube CLI, run against the same ThreadX Win32 source states.
50 valid planted C defects in six families, mapped line by line to the findings each tool produced. Every verdict in this deck comes from the committed reports, not from a summary.
harness-clean 844260e1
bugged 9d699595
52 markers · 50 valid on Win32
Latest Parasoft result: 21 of 50 valid defects with all Flow rules enabled at explicit Thorough depth. MISRA C:2025 added no further strict detections. SonarQube code analysis remains unavailable pending its license.
The question. Given C code with known, deliberately hard defects, do two mainstream tools report the same ones? Each finding was mapped back to the catalog entry it describes, so a verdict is never given for a finding that merely lands near a marker.
Held constant. Same machine, same MSVC v145 Win32 build data, same immutable git tags, same defect catalog. Only the analyzer differed.
Scored. 50 valid candidates after a post-scan audit removed two markers that are not defects on this target.
| True positive | Finding sits at, or on a path directly implicated by, a catalog site, and its message describes that defect's root cause. |
| Partial / indirect | Related but not the cataloged defect (a lost resource instead of a double free, or a hint rather than the dereference). Recorded separately, still counted as a miss for strict recall. |
| Strict miss | Valid catalog defect with no root-cause finding. Direct misses plus partial/indirect findings. |
| Not scored | Findings in pristine upstream ThreadX are real-code findings of that project, not false positives of this evaluation. Compiler warnings are excluded. |
52 marked candidates added between two signed tags.
azure_rtos.sln, Release|Win32, MSVC v145 build data.
Parasoft: paired clean/bugged follow-ups, all Flow rules and Flow + MISRA. SonarQube: earlier DEEP requests were rejected.
Every finding mapped to a catalog entry by line and message.
Two candidates were invalidated; one real issue found in the "clean" baseline.
| Item | Value recorded in the scan protocol |
|---|---|
| Product | C/C++test Standard 2026.1.3 · build 10.7.4.20260825B5553 |
| Interface | Command line — cpptestcli.exe (Standard CLI, no IDE) |
| License | Network license server on the same host; edition server_compliance_edition |
| Host | Windows 11 (10.0.26200) x64, machine id WIN32-2037c5fc |
| Compiler | Visual Studio Professional 2026 (VC toolset v145), passed to the tool as -compiler vc_14_5 |
| Input | Build data collected from azure_rtos.sln@Release|Win32 |
| Output | Native HTML/XML reports per run, normalized findings CSVs, source-linked adjudication matrix and hashes |
| Isolation | Separate worktree per tag, separate tool workspace per run — no state carries between runs |
It performs interprocedural flow analysis and models ThreadX API semantics — byte-pool and block-pool lifecycle, mutex ownership rules, kernel-object create/delete. Several planted defects were designed to be reachable only if the analyzer knows what those APIs mean, which is what makes the results on the concurrency and pool families worth reading.
Original scan: builtin://Flow Analysis Aggressive, 15/50; the preset did not explicitly set Thorough depth. Latest: all 144 Flow checkers, explicit Thorough depth (4), 240-second hotspot limit and the same TX API model.
A separate profile added all 388 shipped MISRA C:2025 checkers. It produced more compliance diagnostics but no additional strict planted-defect TP.
| Item | Value recorded in the scan protocol |
|---|---|
| Product | SonarQube CLI 1.9.0 · build 1.9.0.15656, installed from the official SonarSource install script |
| Interface | Command line — sonar.exe, including sonar analyze with --depth DEEP |
| Authentication | Signed in to SonarQube Cloud (EU region), organization zuwasi; token held in the OS keychain, status reported healthy |
| Project | zuwasi_parasoft-vs-sonarqube-threadx, imported from the public repository with the owner's approval |
| Engines invoked | Secrets scanner (sonar-secrets 2.49.0.12346) — runs locally; and Vortex DEEP code analysis — runs on Sonar's service |
| Commands tried | Whole-scope: --file ×8 --depth DEEP · change-set: --base harness-clean --depth DEEP · analyze secrets |
| Code result | None — Vortex returned 403 Forbidden on every project-backed attempt; all requested files listed as skipped |
| Secrets result | 0 secrets found in each state, on the eight evaluated C files |
The local secrets scanner found no credentials — that is a real, completed analysis, and it says nothing about C defects. The engine that would have analyzed the 50 planted defects is the service-backed deep code engine, and it never ran.
A zero-secrets result is often quoted as "SonarQube found nothing". It is not a code-analysis result and is never used as a score anywhere in this deck.
| File | Lines | Role in the pipeline |
|---|---|---|
| harness_common.h | 247 | Types, frame/header layout, limits, error codes |
| harness_init.c | 463 | Creates threads, queues, pools, mutexes, event group |
| harness_sizearith.c | 306 | Allocation sizing, length arithmetic, ring index, header store |
| harness_memory.c | 316 | Frame lifetime, pools, batch send, cache, retry |
| harness_concurrency.c | 209 | Shared stats, slot hand-off, lock order, job list, queue drain |
| harness_nullpaths.c | 238 | Lookup and validation paths, out-parameters, callbacks |
| harness_taint.c | 185 | Queue-sourced indices, lengths, names, format strings |
| harness_uninit.c | 176 | Pooled memory, stack headers, CRC scratch, snapshots |
The scans also cover pristine upstream ThreadX kernel code in the same solution. Its findings are reported separately and never counted as benchmark false positives.
4 threads · 3 queues · 1 byte pool
1 block pool · 4 mutexes · 1 event group
producer · consumer · logger · maintenance
Trigger data arrives from queue receives (tx_queue_receive into caller memory) and from volatile simulated sensor registers. At runtime the producer only writes documented in-range values, so the application keeps running; statically, a cross-thread queue payload is an unknown value and a volatile read is a fresh unknown.
| Git state | Tag | Commit | Role |
|---|---|---|---|
| Pristine upstream ThreadX | baseline-clean | 93387b0a | Reference for upstream findings |
| Win32 example build adapted to VS2026 / v145 | — | bb0c450c | Buildable state |
| Harness added (intended clean baseline) | harness-clean | 844260e1 | Clean state scanned |
| + 52 planted candidates | bugged | 9d699595 | Detection target |
Both tools scanned the same two materialized states via git worktree checkouts of these immutable tags. For the bugged state the Sonar change-set base was harness-clean, so the change set is the 52 defect sites.
Each plant carries an in-source banner at the defect line:
A finding can therefore be tied to its catalog entry by line and message instead of by judgement.
12 candidates — the emphasized family for this evaluation.
10 — use after release, double release, leaks, interior free.
8 — races, lock inversion, TOCTOU, ownership.
7 valid — unchecked lookup, malloc, out-params, callbacks.
8 — queue-sourced indices, lengths, format strings.
6 — pooled memory, stack headers, CRC, snapshots.
Candidates need interprocedural, path-sensitive or dataflow reasoning — a guard that is not exhaustive, a contract the caller ignores, ownership that crosses a function boundary. Rule-matching alone does not reach most of them.
Defect branches depend on error paths that never fire under the harness's runtime conditions (queue full, allocation failure, abort request), so the demo keeps running while every defect stays reachable in the analysis.
Bounds checks that exist use live values, and defect inputs come from volatile registers or cross-thread queue payloads that cannot be folded to a constant.
Defects sit inside a real sensor-telemetry pipeline built from the same primitives real firmware uses. Candidates marked [TX] additionally require modeling ThreadX semantics — pool lifecycle, mutex ownership, kernel-object create/delete. These probe depth, not pattern coverage.
12 candidates: truncation, wrap, signed/unsigned conversion, sizeof(pointer) misuse, masked-length checks, off-by-one. This is the family the report reads first, and the one the deck shows most code for.
The catalog states what the producer actually writes for each trigger (for example g_sensor_count = tx_time_get() & 0xF, so 0–15). That is how "hard statically, benign at runtime" is verified rather than asserted.
| ID | CWE | Site | Function | Planted defect |
|---|---|---|---|---|
| BUG-001 | 190 → 787 | harness_sizearith.c:26 | sa_alloc_copy | count*4 truncated to UINT16 for the allocation, full length for the copy |
| BUG-002 | 197 | harness_sizearith.c:46 | sa_copy_checked | Magnitude check admits negative lengths, which convert to huge size_t |
| BUG-003 | 193 | harness_sizearith.c:63 | sa_log_write | idx <= LOG_DEPTH off-by-one writes one entry past the ring |
| BUG-004 | 131 | harness_sizearith.c:111 | sa_frame_new | memcpy of sizeof(pointer) leaves the frame partially initialized |
| BUG-005 | 190 | harness_sizearith.c:133 | sa_pack | Invalid — UINT16 payload plus 16 bytes cannot wrap a 32-bit ULONG |
| BUG-006 | 197 | harness_sizearith.c:154 | sa_store_len | Stored length truncated for the allocation, full msg_len used for the copy |
| BUG-007 | 787 | harness_sizearith.c:176 | sa_table_put | Row checked, column not: negative columns walk below the table |
| BUG-008 | 131 | harness_sizearith.c:196 | sa_cfg_snapshot | sizeof(cfg) of the pointer copied instead of the struct |
| BUG-009 | 190 → 690 | harness_sizearith.c:231 | sa_pool_resize | Wrapped pool arithmetic, then an ignored tx_byte_allocate status reaching memset |
| BUG-010 | 170 | harness_sizearith.c:252 | sa_scan_raw | Full-length fill leaves no terminator; the terminator write and strlen both run past the buffer |
| BUG-011 | 704 | harness_sizearith.c:276 | sa_masked_copy | Length checked after a 16-bit mask, copy uses the full 32-bit length |
| BUG-012 | 190/787 | harness_sizearith.c:295 | sa_hdr_store | Configured pad size never validated against sizeof(FRAME_HDR) |
Runtime values keep every one of these benign: the producer writes 0–255 to the length registers and 0–15 to the count register, so no truncation or wrap actually occurs while the pipeline runs. The defects are reachable only for values the analyzer must treat as unknown.
| Family | Valid | Representative candidates and what makes them hard |
|---|---|---|
| Memory lifecycle | 10 | BUG-013 write after tx_byte_release · 016 block released to the byte pool · 017 callee releases and returns success · 021 two cache slots alias one frame, so the flush double-frees |
| Concurrency | 8 | BUG-023 unlocked stats update · 025 lock-order inversion versus net_update_a · 030 queue count checked, then received with the status ignored |
| NULL / validation | 7 | BUG-033 unchecked malloc · 034 out-parameter used after an ignored error · 037 the guard protects the loop but not the returned value · 038 NULL propagated four calls deep |
| Taint / bounds | 8 | BUG-040 length validated against queue capacity · 042 bound checked against a stale table size · 044 received text used as a format string · 046 nibble indexes a 12-entry handler table |
| Uninitialized | 6 | BUG-047 pooled memory read before initialization · 049 a widely called helper stops setting two fields · 051 fill loop stops one short of the compared entry |
All four threads are created with tx_thread_create inside harness_init.c. A tool that does not model ThreadX thread creation cannot see that the functions run concurrently at all, and the eight races become invisible rather than merely hard.
Eight candidates additionally require ThreadX API semantics: byte-pool memory is not zeroed, a block-pool block must be released with tx_block_release, a mutex put requires ownership, a created TX_TIMER must be deleted. They probe RTOS knowledge rather than pattern coverage.
Two plants (BUG-008, BUG-048) are things MSVC itself warns about — C4700 class defects. They are useful as a floor: any flow analyzer that misses the compiler-visible cases would be reported as such.
Trigger: producer_entry → harness_tick → sa_alloc_copy(count = g_sensor_count), a volatile simulated register.
Both expressions come from the same value through different routes. For any count above 16383 the truncated 16-bit size is smaller than the copy, so the heap buffer is overrun. Detection needs integer-range reasoning that a 32-bit input can exceed that bound, then correlation of the two expressions.
No finding on either line, and no finding anywhere in this function. The scan reported two unrelated diagnostics in this file — one on the unused copy in sa_copy_checked and one on the ring index at line 68.
No code scan The file was among the eight requested; the request was rejected with 403 before analysis. Not a miss — an absence of any result.
The subtlety: LOG_DEPTH is 32 and the ring holds 32 entries, so the correct guard is <. With <=, index 32 passes and writes past the end of the array.
The message names the exact bound the plant violates, which is why this counts as a root-cause true positive rather than a nearby hint.
The index is derived (code >> 3) from a volatile value, so the analyzer must reason about the derived range against a macro bound instead of folding a constant.
No code scan 403, file skipped.
On the clean state Parasoft reported BD-PB-OVERFRD at line 161 — reading up to 65535 bytes from the 128-byte sa_src. In the bugged state the guard and the + 1u are gone and the copy is worse, but the function produced no finding at all. The plant turned a reported condition into a strict miss.
Concrete counterexample: msg_len = 0x1003F (65599). The masked value is 63, so the check passes; the copy then moves 65599 bytes into a 64-byte stack buffer.
The scan reports BD-PB-MEMOPT at line 279 — "call to memcpy may be optimized out as the dst buffer is not used later". That diagnostic is about the unused copy, not about the length mismatch, so it is not credited as a detection of this defect.
A finding that lands on the defect line but describes a different problem is exactly how "detected" counts get inflated. Here it is disclosed and scored as a miss.
No code scan 403, file skipped.
Two findings, at the failing send and at the unconditional free — the loop-carried use-after-free and the second free it can cause.
The analyzer tracked the block across free() and reported both later uses — the failing send inside the loop and the unconditional free below it. Nothing in the message needs interpretation: it names the object that was released.
No code scan 403, file skipped.
Callers: the producer thread calls stats_record_fast while the consumer thread updates the same field through the locked stats_record. The two functions are adjacent, in one file, on one shared field.
“The g_stats variable is used when locks are not held on stats_mutex.” At line 39 the read-modify-write omits the mutex used by its sibling function. This is the newly counted BUG-023 TP.
The clean state also reports an unlocked g_stats callback at harness_init.c:65; that is a separate, unplanted baseline concern and is disclosed in the clean review.
This latest profile enabled every installed Flow checker, including BD-TRS-MLOCK, and used the TX API model. The earlier shipped Aggressive preset missed this site; the full-rule follow-up reports it at the planted update.
No code scan 403, file skipped.
The message carries the whole argument: the bound in the code is 128, the buffer is 64.
A bound check is present and looks reassuring; the tool compared it with the array's real size.
The finding lands at the read site, in a different function from the plant — whole-program initialization tracking.
Two diagnostics on one line, both describing the actual defect: the pointer was never assigned because the return value was discarded.
| State | Configuration | UTC start | UTC end | XML findings | Of which harness |
|---|---|---|---|---|---|
| harness-clean | builtin://Flow Analysis Aggressive | 18:31:58Z | 18:34:05Z | 90 | 6 |
| bugged | builtin://Flow Analysis Aggressive | 18:34:06Z | 18:36:16Z | 111 | 27 |
The clean run is the false-positive baseline and the bugged run is the detection target; both use the same configuration, compiler and build data, so the only difference between them is the planted diff.
Each state was materialized as a git worktree at an immutable tag, and each run got its own workspace, so no cached analysis leaked from one run into the next. The compiler identifier was resolved once with -list-compilers on the first licensed run and reused.
Two XML reports for this original pair — plus four more from the latest all-Flow and MISRA profiles — with run logs, UTC timestamps, exit codes, normalized CSV exports in the repository, and SHA-256 hashes of every raw report. The raw reports themselves stay outside the repository.
| State | Command | Exit | Outcome |
|---|---|---|---|
| clean | analyze --file ×8 --depth DEEP (no project) | 0 | No project configured; Vortex skipped |
| clean | analyze secrets (whole worktree) | 1 | Timed out at the CLI's 30-second limit |
| clean | analyze --file ×8 --depth DEEP (no project) | 0 | No project configured; Vortex skipped |
| clean | analyze secrets (8 C files) | 0 | 0 secrets |
| bugged | analyze --base harness-clean --depth DEEP | 0 | No project configured; Vortex skipped |
| bugged | analyze --file ×8 --depth DEEP | 0 | No project configured; Vortex skipped |
| bugged | analyze secrets (8 C files) | 0 | 0 secrets |
| clean | --project … --file ×8 --depth DEEP | 1 | Vortex 403 · 8 files skipped |
| bugged | --project … --base harness-clean --depth DEEP | 1 | Vortex 403 · 6 files skipped |
| bugged | --project … --file ×8 --depth DEEP | 1 | Vortex 403 · 8 files skipped |
| both | sonar system status | 0 | Auth healthy · VORTEX: Not entitled |
Sign in → verify status → import the public repository as a project → attempt whole-file DEEP analysis → attempt change-set analysis against harness-clean → narrow the secrets scan to the eight evaluated files after the whole-worktree scan timed out → re-check status. Nothing was skipped in the attempt order; the failures are part of the record.
| Source | harness-clean 844260e1 vs bugged 9d699595 |
| Target | azure_rtos.sln@Release|Win32 · MSVC v145 / vc_14_5 |
| Scope | 214 files · isolated CLI workspace for every scan |
| Flow rules | All 144 installed checker IDs; 25 aggressive/unvalidated options plus two NOTINIT options |
| Depth / time | Thorough (depthOfAnalysis=4) · 240-second hotspot limit |
| RTOS semantics | Same custom ThreadX multithreading API model in both profiles |
| Second profile | All 388 shipped MISRA C:2025 checkers added; other settings held constant |
| Profile | Clean | Bugged | TP | Timeouts C/B |
|---|---|---|---|---|
| All Flow | 324 / 26 | 354 / 49 | 21 | 187 / 183 |
| Flow + MISRA C:2025 | 4,661 / 558 | 4,745 / 615 | 21 | 210 / 206 |
Clean/bugged counts are total XML findings and harness entries. MISRA aliases and compliance diagnostics are not additional planted-defect TPs.
42% strict recall · explicit Thorough depth
27 direct misses · one partial · one indirect
Findings are not unique defects; each TP was adjudicated at its source site.
Pending license entitlement; prior code requests returned 403
| Profile | Bugged total / harness | Strict TP | Partial / indirect | Strict FN |
|---|---|---|---|---|
| Initial builtin Aggressive (depth not explicit) | 111 / 27 | 15 | 1 / 1 | 35 |
| All 144 Flow · Thorough / 240s | 354 / 49 | 21 | 1 / 1 | 29 |
| All Flow + 388 MISRA C:2025 rules | 4,745 / 615 | 21 | 1 / 1 | 29 |
BUG-021 (partial): a lost-resource signal, not the planted double free. BUG-035 (indirect): BD-PB-CC flags the late null check, not the dereference. Both remain strict misses.
| Family | Valid | True positives | Partial / indirect | Direct misses | What the scan saw |
|---|---|---|---|---|---|
| Size arithmetic | 11 | 2 | 0 | 9 | Ring and raw-buffer bounds; truncation, wrap, masked length and sizeof misuse remain missed |
| Memory lifecycle | 10 | 3 | 1 partial | 6 | Use-after-free, interior free and retry leak; no root-cause finding for six other candidates |
| Concurrency | 8 | 5 | 0 | 3 | New BD-TRS-MLOCK finding for BUG-023; BUG-024, 027 and 030 remain missed |
| NULL / validation | 7 | 5 | 1 indirect | 1 | Unchecked lookup, allocation, out-param and deep NULL propagation |
| Taint / bounds | 8 | 3 | 0 | 5 | Buffer overflow and table bounds; indices, format string and allocation size remain missed |
| Uninitialized | 6 | 3 | 0 | 3 | Thorough follow-up adds BUG-048; pooled memory and partial-array cases remain missed |
| Total | 50 | 21 | 1 / 1 | 27 direct | 29 strict FN, including partial and indirect |
The all-Flow profile adds one race detection without closing the remaining size-arithmetic, memory-contract, or queue/list concurrency gaps. The score is source-adjudicated, not a count of all emitted warnings.
Size arithmetic was the stated focus of this evaluation, and it produced the largest miss block: truncation, wrap, signed-to-unsigned conversion, masked bounds and sizeof(pointer) misuse all went unreported.
The five root-cause findings require the ThreadX API model and the full Flow checker set. Three planted races/queue defects still have no matching finding.
| Rule | Location | Message as emitted by Parasoft | Catalog |
|---|---|---|---|
| BD-PB-ARRAY | sizearith.c:68 | Possibly accessing array "log_ring" out of bounds at index [0...32]. Correct index(es): [0...31] | BUG-003 |
| BD-PB-ARRAY | sizearith.c:259 | Possibly accessing array "raw" out of bounds at index [2...64]. Correct index(es): [0...63] | BUG-010 |
| BD-RES-FREE | memory.c:172, :180 | Usage of freed resource "batch" | BUG-018 |
| BD-RES-INVFREE | memory.c:237 | Invalid memory freeing: "body" does not point to the beginning of a resource in memory | BUG-020 |
| BD-RES-LEAKS | memory.c:299, :316 | Memory not deallocated: p | BUG-022 |
| BD-PB-NP | nullpaths.c:96 | "ch" may possibly be null | BUG-032 |
| BD-PB-CHECKRETGEN BD-PB-NP | nullpaths.c:111, :115 | Value returned from method "malloc" should be checked · "s" may possibly be null | BUG-033 |
| BD-PB-NOTINIT | nullpaths.c:154 | Non-initialized "f" is used | BUG-034 |
| BD-PB-CC | nullpaths.c:170 | Condition "cfg == 0" always evaluates to false | BUG-035 · indirect |
| BD-PB-NOTINIT | nullpaths.c:211 | Non-initialized "v" is used | BUG-037 |
| BD-PB-NP | nullpaths.c:226 (×2) | "c" may possibly be null | BUG-038 |
| BD-PB-NOTINIT | uninit.c:88 | Non-initialized "h.flags" is used | BUG-049 |
| BD-PB-NOTINIT BD-RES-INVFREE | uninit.c:130 | Non-initialized "u" is used · Invalid memory freeing: "u" is not initialized in heap memory | BUG-050 |
| BD-PB-OVERFWR | taint.c:75 | Overflow when writing to buffer "dst" (UCHAR[64], 64 bytes large). Number of bytes written 4 <= len <= 128 | BUG-040 |
| BD-PB-ARRAY | taint.c:104 | Possibly accessing array "slot_values" out of bounds at index [0...7]. Correct index(es): [0...3] | BUG-042 |
| BD-PB-ARRAY | taint.c:182 | Possibly accessing array "handlers" out of bounds at index [0...15]. Correct index(es): [0...11] | BUG-046 |
| BD-RES-LEAKS | memory.c:244, :260 | Memory not deallocated: base · Memory not deallocated: ((FRAME_STRUCT[2])frame_cache)[1] | BUG-021 · partial |
| BD-TRS-MLOCK | concurrency.c:39 | The "g_stats" variable is used when locks are not held on "stats_mutex" | BUG-023 · new TP |
| BD-TRS-ORDER | concurrency.c:75 | Possible deadlock - acquiring locks "net_mutex_2", "net_mutex_1" in different orders | BUG-025 |
| BD-TRS-THRDR | concurrency.c:111 | The global variable "g_ready" is used without holding a lock | BUG-026 |
| BD-TRS-REVLOCK | concurrency.c:155 | The "net_mutex_1" lock is released, but it has not been acquired | BUG-028 |
| BD-TRS-THRDR | concurrency.c:166 | The global variable "g_inited" is used without holding a lock | BUG-029 |
| BD-PB-NOTINIT | uninit.c:69 | Non-initialized "h.flags" is used | BUG-048 |
Light-blue rows were absent from the original Aggressive run: BUG-025…029 first appeared with the ThreadX API model at Thorough depth, BUG-048 with the added NOTINIT options, and only BUG-023 needed the full all-Flow profile. MISRA C:2025 repeats the BUG-023 site as MISRAC2025-DIR_5_1-a and is counted once, not as another detection.
| Class | Candidates |
|---|---|
| Concurrency — 3 direct | BUG-024 · BUG-027 · BUG-030 (BUG-023, 025, 026, 028, 029 detected) |
| Size arithmetic | BUG-001 truncation · 002 signed/unsigned · 004 sizeof(pointer) · 006 truncated stored length · 007 unchecked column · 008 sizeof(pointer) config · 009 wrapped pool arithmetic · 011 masked length · 012 unvalidated pad |
| Memory lifecycle | BUG-013 write after release · 014 double release · 015 early-return leak · 016 wrong pool release · 017 release-then-success · 019 timer leak · 021 partial signal |
| Taint / bounds | BUG-039 unmasked index · 041 strcpy into 16-byte label · 043 unclamped loop · 044 format string · 045 legacy allocation without terminator |
| Uninitialized | BUG-047 pooled memory · 051 fill loop stops one short · 052 snapshot read before zeroing |
| NULL paths | BUG-036 unchecked callback · BUG-035 remains an indirect signal |
The planted size addition uses a UINT16 payload, so the maximum total is 65535 + 12 + 4 = 65,551. ULONG is 32-bit on Win32, so the addition cannot wrap, and the resulting allocation covers the payload copy. Excluded from valid-defect scoring — and Parasoft correctly reported nothing here.
The checked accessor and the primary lookup share the same range guard and the same table slot. A non-NULL accessor result therefore proves the primary result is non-NULL. Again, no finding here is the right answer.
The allocation is sized msg_len + 1, but the 128-byte source can still be over-read for lengths above 128. This issue predates the planted diff and is not credited to any candidate.
| Rule | Location | Review outcome |
|---|---|---|
| BD-PB-MEMOPT | sizearith.c:51 | Scratch copy with no observable output — intentional |
| BD-PB-CC | sizearith.c:260 | Length already clamped below 64; redundant check |
| BD-PB-MEMOPT | sizearith.c:277 | Scratch copy — intentional |
| BD-PB-VOVR | memory.c:61 | Initial value overwritten before any read |
| BD-PB-MEMOPT | taint.c:71 | Scratch copy — intentional |
| BD-TRS-MLOCK | init.c:65 | Unplanted: clean callback updates g_stats without stats_mutex |
| BD-TRS-THRDR | init.c:215 | ThreadX-managed queue receive, not an app data race |
| BD-TRS-THRDR | sizearith.c:66–68, 84 | File-static ring used only by the logger thread |
| BD-API-STRSIZE | sizearith.c:161 | Duplicate of the known source-buffer over-read |
The clean reference already has a real 128-byte source over-read and a separate unlocked callback. They are not planted TPs. Some concurrency warnings are contextual or assumption-dependent; MISRA also emits compliance diagnostics. The 558 MISRA clean entries are not 558 unique defects.
Authentication was healthy the whole time. The CLI reached the service, the project existed, the commands were accepted — and the code-analysis engine refused every job.
Sonar's documentation states that Vortex is part of Sonar Agent Essentials, a separate subscription for SonarQube Cloud annual Team or Enterprise plans. The zuwasi organization has no Agent Essentials entitlement. No CLI flag, region switch or project setting can grant it, and none was attempted after this was established.
| Not zero findings | Every requested file is marked skipped. Nothing was analyzed, so nothing was found or missed. |
| Not a Sonar score | No TP, FP or FN value for SonarQube exists in this experiment, in either state. |
| Real but different | The local secrets scanner did complete: 0 secrets in each state, on the same eight files. It does not analyze C defects. |
8 files requested → 8 skipped, exit 1
6 changed files → 6 skipped, exit 1
Auth healthy · Vortex not entitled
Every table reports it as pending license, never zero
| Profile / tool | Clean total / harness | Bugged total / harness | Valid | TP | Partial / indirect | Strict FN | Status |
|---|---|---|---|---|---|---|---|
| Initial builtin Aggressive · depth not explicit | 90 / 6 | 111 / 27 | 50 | 15 | 1 / 1 | 35 | Historical baseline |
| All 144 Flow · Thorough / 240s | 324 / 26 | 354 / 49 | 50 | 21 | 1 / 1 | 29 | Completed; timeouts remain |
| All Flow + 388 MISRA C:2025 | 4,661 / 558 | 4,745 / 615 | 50 | 21 | 1 / 1 | 29 | No added strict TP |
| SonarQube CLI · DEEP code analysis | N/A | N/A | 50 | N/A | N/A | N/A | Pending license; prior 403 |
In the latest all-Flow, ThreadX-aware Thorough scan, Parasoft reported 21 of 50 valid planted root causes and missed 29. The new full-rule scan found BUG-023; MISRA C:2025 aliases that same message and added no strict TP.
This is not a product ranking: one Win32 target, one C/C++test version and TX model, and one entitlement state. All profiles still had Flow timeouts. The SonarQube code result remains unavailable pending license, never zero.
Quote the profile and settings: “All installed Flow rules at Thorough/240s found 21/50; adding shipped MISRA C:2025 rules did not increase strict detections.” SonarQube is still pending an entitled code scan.
The MISRA profile swaps the second committed properties file; run-threadx-allflow-misra-240.ps1 drives all four scans and PARASOFT_RUN_GUIDE.md documents every step.
| baseline-clean | 93387b0a |
| harness-clean | 844260e1 |
| bugged | 9d699595 |
| bugged XML | b1f32ffd81dfc3b0…e96f8 |
| clean XML | 235fd61d0eafa584…539c3 |
Original-pair hashes: reports/parasoft/SHA256SUMS.txt. Latest four runs: raw-report and console SHA-256 in threadx-allflow-misra-240/run-summary.csv.
| PLANTED_BUGS.md | 52 candidates, validity audit, trigger paths |
| SCAN_PROTOCOL.md · PARASOFT_RUN_GUIDE.md | Versions, commands, UTC times, exit codes, export steps |
| …/threadx-allflow-misra-240/ | Latest matrix, four findings CSVs, run summary, clean review |
| …/adjudication/ · …/parasoft/ | Original Aggressive matrix and its findings exports |
| …/sonarqube/* | CLI JSON, secrets logs, outcomes, system status |
| ID | CWE | Site | All-Flow 240s | Sonar | Adjudication note |
|---|---|---|---|---|---|
| BUG-001 | 190/787 | sa:26 | FN | N/A | No matching flow finding |
| BUG-002 | 197 | sa:46 | FN | N/A | Only an unrelated unused-copy finding near this routine |
| BUG-003 | 193 | sa:63 | TP | N/A | BD-PB-ARRAY reports ring index 32 |
| BUG-004 | 131 | sa:111 | FN | N/A | No matching partial-initialization finding |
| BUG-005 | 190 | sa:133 | Invalid | N/A | UINT16 plus 16 cannot wrap 32-bit ULONG |
| BUG-006 | 197 | sa:154 | FN | N/A | No matching allocation-versus-copy finding |
| BUG-007 | 787 | sa:176 | FN | N/A | No matching unchecked-index finding |
| BUG-008 | 131 | sa:196 | FN | N/A | No matching partial-configuration-copy finding |
| BUG-009 | 190/690 | sa:231 | FN | N/A | No overflow or unchecked ThreadX allocation finding |
| BUG-010 | 170 | sa:252 | TP | N/A | BD-PB-ARRAY reports raw[64] |
| BUG-011 | 704 | sa:276 | FN | N/A | Unused-copy report does not identify the masked-length mismatch |
| BUG-012 | 190/787 | sa:295 | FN | N/A | No matching undersized-header-buffer finding |
| BUG-013 | 416 | mem:45 | FN | N/A | ThreadX release not treated as invalidating the frame |
| BUG-014 | 415 | con:203 | FN | N/A | No finding on the caller's unconditional second release |
| BUG-015 | 401 | mem:85 | FN | N/A | No matching early-return leak finding |
| BUG-016 | 762 | mem:125 | FN | N/A | Wrong ThreadX pool release not reported |
| BUG-017 | 415 | mem:148 | FN | N/A | No finding on the release-on-success contract |
| BUG-018 | 416 | mem:175 | TP | N/A | BD-RES-FREE — use of the freed batch |
| BUG-019 | 404 | mem:206 | FN | N/A | No matching ThreadX timer lifecycle finding |
| BUG-020 | 761 | mem:233 | TP | N/A | BD-RES-INVFREE — freeing an interior pointer |
| BUG-021 | 415 | mem:257 | Partial | N/A | Related lost-resource effect at the alias assignment, not the double free |
| BUG-022 | 401 | mem:311 | TP | N/A | BD-RES-LEAKS — failed-retry buffer leaks |
| BUG-023 | 362 | con:37 | TP | N/A | BD-TRS-MLOCK — unlocked g_stats update (con:39) |
| BUG-024 | 366 | con:56 | FN | N/A | No matching TOCTOU report; init.c:225 leak is a different path |
| BUG-025 | 833 | con:87 | TP | N/A | TX model: BD-TRS-ORDER lock-order deadlock (con:75) |
| BUG-026 | 364 | con:108 | TP | N/A | TX model: BD-TRS-THRDR on unsynchronized g_ready (con:111) |
Legend: sa = harness_sizearith.c · mem = harness_memory.c · con = harness_concurrency.c. Verdicts reproduce the committed threadx-allflow-misra-240/DETECTION_MATRIX.csv; the original-run matrix stays in reports/adjudication/.
| ID | CWE | Site | All-Flow 240s | Sonar | Adjudication note |
|---|---|---|---|---|---|
| BUG-027 | 362 | con:134 | FN | N/A | No matching list race finding |
| BUG-028 | 667 | con:153 | TP | N/A | TX model: BD-TRS-REVLOCK — mutex released unacquired (con:155) |
| BUG-029 | 366 | con:164 | TP | N/A | TX model: BD-TRS-THRDR on unsynchronized g_inited (con:166) |
| BUG-030 | 366 | con:190 | FN | N/A | No queue-status or unchecked-read finding |
| BUG-031 | 476 | np:74 | Invalid | N/A | Same range check means a non-NULL mirror implies a non-NULL primary |
| BUG-032 | 476 | np:94 | TP | N/A | BD-PB-NP — possible null channel dereference |
| BUG-033 | 690 | np:113 | TP | N/A | Allocation return-check and null-dereference findings |
| BUG-034 | 457 | np:151 | TP | N/A | BD-PB-NOTINIT — use of unassigned frame pointer |
| BUG-035 | 476 | np:166 | Indirect | N/A | BD-PB-CC flags the late null check as always false; no direct dereference finding |
| BUG-036 | 690 | np:184 | FN | N/A | No matching unchecked-callback finding |
| BUG-037 | 476 | np:200 | TP | N/A | BD-PB-NOTINIT — uninitialized return value |
| BUG-038 | 476 | np:224 | TP | N/A | BD-PB-NP — possible null channel dereference |
| BUG-039 | 129 | ta:56 | FN | N/A | No matching calibration-table bounds finding |
| BUG-040 | 787 | ta:71 | TP | N/A | BD-PB-OVERFWR — destination overflow |
| BUG-041 | 120 | ta:86 | FN | N/A | No matching unbounded-string-copy finding |
| BUG-042 | 129 | ta:101 | TP | N/A | BD-PB-ARRAY — index 4 through 7 in a 4-entry table |
| BUG-043 | 787 | ta:116 | FN | N/A | No matching accumulator-array bounds finding |
| BUG-044 | 134 | ta:133 | FN | N/A | No matching tainted-format-string finding |
| BUG-045 | 680/787 | ta:151 | FN | N/A | No matching allocation-size / string-copy finding |
| BUG-046 | 129 | ta:179 | TP | N/A | BD-PB-ARRAY — handler index 12 through 15 |
| BUG-047 | 457 | un:41 | FN | N/A | No finding on pooled frame flags |
| BUG-048 | 457 | un:63 | TP | N/A | BD-PB-NOTINIT — non-initialized h.flags (un:69) |
| BUG-049 | 457 | un:17 | TP | N/A | BD-PB-NOTINIT traces the helper's missing flags initialization |
| BUG-050 | 457 | un:127 | TP | N/A | Uninitialized-use and invalid-free findings at slot_track |
| BUG-051 | 457 | un:143 | FN | N/A | No finding on the uninitialized CRC entry |
| BUG-052 | 457 | un:167 | FN | N/A | No matching uninitialized stats-field finding |
Legend: np = harness_nullpaths.c · ta = harness_taint.c · un = harness_uninit.c · mem = harness_memory.c · con = harness_concurrency.c.
Verify any row: PLANTED_BUGS.md · *-findings.csv · latest DETECTION_MATRIX.csv · PARASOFT_RUN_GUIDE.md · run-summary.csv hashes. Repository: github.com/zuwasi/parasoft-vs-sonarqube-threadx (MIT). Prepared by Daniel Liezrowice, ESL — Engineering Software Lab, 2026-10-08.