Skip to content

Misc fixes from Coverity and Sonar audits (cleanup batch) - #22299

Closed
ktsaou wants to merge 23 commits into
netdata:masterfrom
ktsaou:coverity-cleanup-2026-04
Closed

Misc fixes from Coverity and Sonar audits (cleanup batch)#22299
ktsaou wants to merge 23 commits into
netdata:masterfrom
ktsaou:coverity-cleanup-2026-04

Conversation

@ktsaou

@ktsaou ktsaou commented Apr 28, 2026

Copy link
Copy Markdown
Member

Summary

Audit-driven fixes for outstanding Coverity defects and SonarCloud findings,
landed as small focused commits.

  • claim: drop unreachable curl_slist_free_all in the
    curl_slist_append failure path. Coverity CID 503493 (DEADCODE):
    headers is initialised NULL and only assigned after the append
    succeeds; the cleanup if(headers) is unreachable.
  • netipc: harden vendored Coverity paths
    (protocol/service/transport on POSIX and Windows).
  • cgroups: fix unsigned underflow OOB in
    convert_cgroup_to_systemd_service. Sonar c:S3519 (BLOCKER):
    while (len--) on size_t wraps to SIZE_MAX when the input has no
    separator, causing s[SIZE_MAX] = '\0' -- replaced both reverse scans
    with strrchr() + explicit found-pointer checks.

The remaining outstanding queue items (currently dominated by guard-
propagation and z-allocator-unawareness false positives) will be
addressed in subsequent commits on this branch.

Test plan

  • CI build passes (gcc + clang)
  • No new Coverity defects introduced in the touched files
  • No new Sonar findings introduced in the touched files
  • cgroups.plugin smoke-tested on a host with systemd cgroups

Summary by cubic

Cleans up Coverity/Sonar findings and hardens netipc across POSIX/Windows. Enables strict TLS hostname verification in the MQTT WSS client with IP-literal support and opt-in overrides for self-signed/hostname mismatches.

  • Bug Fixes
    • aclk/mqtt_wss: enable TLS peer identity checks; try X509_VERIFY_PARAM_set1_ip_asc for IP literals and fall back to X509_VERIFY_PARAM_set1_host; accept X509_V_ERR_HOSTNAME_MISMATCH/X509_V_ERR_IP_ADDRESS_MISMATCH when MQTT_WSS_SSL_ALLOW_SELF_SIGNED is set; respect MQTT_WSS_SSL_DONT_CHECK_CERTS.
    • claim: removed unreachable curl_slist_free_all on append failure (Coverity CID 503493).
    • claim/ui (Windows): removed unreachable break in WndProc default case.
    • cgroups.plugin: fixed unsigned-underflow/OOB in convert_cgroup_to_systemd_service using strrchr() with explicit checks.
    • apps.plugin: replaced single-iteration while (start) with if (start) in comm_from_cmdline_sanitized.
    • ebpf.plugin: compute zombie as exit - release_task (drop unsigned unary minus and in-place mutation).
    • pulse: use int loop variable when negating to STREAM_HANDSHAKE enum.
    • netipc protocol/service: added safe header+payload length helpers; guarded 32-bit size_t overflows; validated response sizes and buffer bounds; added null checks in server init; safer client/server buffer sizing.
    • netipc POSIX SHM/UDS: safer stale-path recovery—use open with O_NOFOLLOW/lstat, verify inode/type before unlink, unlink only on proven-stale sockets (ECONNREFUSED); validated SHM region size math.
    • netipc Windows SHM/Named Pipes: mirrored overflow/capacity guards; validated region sizing; computed message lengths with overflow checks before send/receive.
    • exporting: fixed UNIT_TESTING early returns to match function signatures—aws_kinesis_connector_worker and pubsub_connector_worker now return NULL; exporting_main uses a bare return.
    • tests: statsd-stress frees per-packet strings/arrays and closes the socket on sendto() failure; removed dead post-loop cleanup.
    • mcp: dropped redundant null guard in mcp_query_interrupt_callback; marked parameter unused.
    • go.d/rethinkdb: removed degenerate switch in sortRethinkRows; always sort by DurationMs.
    • api: create management API key file with mode 0600.
    • sqlite: create recovery marker files with mode 0600.
    • os: create lock files with mode 0600.
    • socket: drop exec bit on UNIX socket files; chmod 0666 instead of 0777.
    • nd-mcp.py: re-raise asyncio.CancelledError during reconnect-delay; cancel in-flight wait/retry tasks and clear the event before re-raising; after asyncio.wait, cancel and drain pending tasks via asyncio.gather(return_exceptions=True), re-raising non-cancellation exceptions.

Written for commit 7623386. Summary will update on new commits. Review in cubic

ktsaou added 3 commits April 28, 2026 08:41
Coverity CID 503493 (DEADCODE): in send_curl_request(), the failure path of
curl_slist_append() called curl_slist_free_all() through `if(headers)` -- but
`headers` is initialized to NULL on entry and only assigned after the append
succeeds. The condition can never be true and the free is unreachable.

Drop the dead block. The remaining cleanup (curl_easy_cleanup, can_retry=false,
return false) is unchanged and correct.
Sonar c:S3519 (BLOCKER): the reverse-scan loops in
convert_cgroup_to_systemd_service() used `while (len--)` on a `size_t`,
so when the input contained no separator the loop ran to completion and
the post-decrement on len == 0 wrapped to SIZE_MAX. The subsequent
`if (len)` was true and `s[len] = '\0'` wrote far out of bounds.

The dot-search was the explicitly flagged path; the slash-search had the
same wrap pattern but was accidentally benign because `&s[SIZE_MAX + 1]`
wrapped back to `&s[0]`.

Replace both reverse scans with strrchr() and an explicit non-NULL,
non-leading check. Preserves the existing behavior for valid inputs
(separator at index > 0 truncates / repositions; separator at index 0 or
absent leaves the string unchanged) and removes the unsigned-underflow
path.
@github-actions github-actions Bot added area/collectors Everything related to data collection area/claim collectors/cgroups labels Apr 28, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 9 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.
Architecture diagram
sequenceDiagram
    participant Client as Client Application
    participant Service as IPC Service Layer
    participant Transport as IPC Transport (SHM/UDS)
    participant Kernel as OS Kernel / Filesystem

    Note over Service,Kernel: Server Initialization & Recovery
    Service->>Service: NEW: Strict server_init_raw() validation (null checks)
    Service->>Transport: Create Server Endpoint
    
    Transport->>Kernel: CHANGED: lstat() and open(O_NOFOLLOW)
    Kernel-->>Transport: File/Socket metadata
    
    alt Path exists but is stale
        Transport->>Transport: CHANGED: Verify Inode matching
        Transport->>Kernel: NEW: unlink_same_file() (atomic path guard)
    else Path is active/ambiguous
        Transport->>Transport: Treat as live (prevent overwrite)
    end

    Note over Client,Kernel: Connection & Negotiation
    Client->>Service: Request Session Handshake
    
    Service->>Service: NEW: header_payload_len() overflow checks
    alt Valid Capacity
        Service->>Service: CHANGED: Safe buffer allocation (ensure_buffer)
        Service-->>Client: Accept (Negotiated Max Payload)
    else Overflow/Limit Exceeded
        Service-->>Client: Reject (NIPC_ERR_OVERFLOW)
    end

    Note over Client,Kernel: Data Exchange (Request/Response)
    Client->>Service: Send Data Payload
    
    Service->>Service: NEW: Validate size_t vs uint32_t (32-bit platform guards)
    
    alt Payload size within limits
        Service->>Transport: CHANGED: transport_send() with validated msg_len
        Transport->>Kernel: Write to SHM / Socket
        Kernel-->>Service: Success
    else Payload too large
        Service-->>Client: Return NIPC_ERR_LIMIT_EXCEEDED
    end

    Note over Service,Transport: Response Handling
    Service->>Service: NEW: Post-dispatch response length validation
    opt Response > negotiated limit
        Service->>Service: NEW: server_note_response_capacity()
    end
Loading

Comment thread src/libnetdata/netipc/src/transport/posix/netipc_shm.c Dismissed
Comment thread src/libnetdata/netipc/src/transport/posix/netipc_uds.c Dismissed
Sonar c:S3584 / c:S1763: spam_thread() returned directly when sendto()
failed, leaking the strdup'd per-packet strings, the packets and lengths
arrays, and the UDP socket fd. The cleanup at the end of the function
sat after an unconditional `for (;;)` loop, so it was unreachable.

Move the cleanup into the sendto() failure path (free each packets[j]
string, then free packets, free lengths, close the socket) and drop the
post-loop dead code. The function still has the same single exit path
(early return on send failure); GCC recognises the trailing infinite
loop and does not require a fall-through return.
ktsaou added 3 commits April 28, 2026 11:35
…NG branch

Sonar c:S935 (CRITICAL): aws_kinesis_connector_worker() is declared
void *, but the conditional test-only early exit inside #ifdef
UNIT_TESTING used a bare `return;`. Per C99/C11 6.8.6.4, a return without
an expression is only permitted in a function whose return type is void.
Production builds skip this block, but enabling UNIT_TESTING fails to
compile.

Change the bare return to `return NULL;` so test builds satisfy the
function signature; production control flow is unchanged.
…anch

Sonar c:S935 (CRITICAL): pubsub_connector_worker() is declared void *,
but the conditional test-only early exit inside #ifdef UNIT_TESTING used
a bare `return;`. Per C99/C11 6.8.6.4, a return without an expression is
only permitted in a function whose return type is void. Production
builds skip this block, but enabling UNIT_TESTING fails to compile.

Same pattern as aws_kinesis.c:214 fixed in the previous commit.
…TING branch

Sonar c:S935 (CRITICAL): exporting_main() is declared void, but the
conditional test-only early exit inside #ifdef UNIT_TESTING used
`return NULL;`, which is invalid for a void-returning function per
C99/C11 6.8.6.4. Production builds skip the block, but enabling
UNIT_TESTING fails to compile.

Change the conditional return to a bare `return;`, and remove the stale
"@return It always returns NULL" line from the function's docstring
(left behind from when the signature was void *).

Together with the previous two commits, this closes the c:S935 trio
across the exporting subsystem (aws_kinesis, pubsub, exporting_engine).
ktsaou added 3 commits April 28, 2026 11:48
Sonar c:S2612 (MAJOR vulnerability): the management API key file was
created with `open(..., O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 444)`. The
literal `444` is decimal, not octal, which equals octal 0674 = rw-rwxr--.
Group gets read+write+execute and others get read on a file that stores
the management API key (a UUID granting admin endpoint access). Anyone
with local read on the host can lift the key.

Even if the original intent was octal `0444` (world-readable), a
secret-key file should not be readable by group or others. Use `0600`
(owner read+write only), the standard mode for secrets.
Sonar c:S2612: mark_database_to_recover() created the
.netdata-meta.db.recover / .delete marker with `open(..., O_WRONLY |
O_CREAT | O_TRUNC | O_CLOEXEC, 444)`. The literal `444` is decimal,
which is octal 0o674 (rw-rwxr--) -- group rwx + others r. Same
decimal-not-octal typo as the previous api_v1_manage.c commit.

The file is a marker created and immediately closed (no body written);
only its existence matters at next-startup recovery detection. It does
not need group or other access. Use mode 0600 -- minimum permissions
needed by the owning netdata process, eliminates the c:S2612 violation.
Sonar c:S2612: file_lock_get() created the advisory lock file with
mode 0666 (rw-rw-rw-). Netdata runs as a single dedicated user, so
group/other access is unnecessary and adds latent risk if any local
user can interfere with the lock file.

flock(2) is purely advisory and does not enforce by mode, but the open()
permission still controls who can create/access the file. Owner-only
0600 keeps the locking behavior intact for the netdata user while
preventing unrelated local users from creating or opening the file.

The function is currently unused (its caller in src/daemon/main.c is
commented out), but it is exposed in the public header and may be
revived for single-instance enforcement; tightening the default now
avoids carrying permissive bits forward.
… -> 0666)

Sonar c:S2612: create_listen_socket_unix() chmod'd the bound UNIX socket
file to 0777. For UNIX domain socket files only the read/write
permissions affect client connect() access -- the execute bit is unused.
0777 and 0666 are functionally identical for socket connect.

Both callers (web API and statsd) intentionally allow arbitrary local
clients to connect, so the broad rw permission is preserved with 0666.
The execute bit was misleading and unnecessary; remove it and update
the explanatory comment.
Comment thread src/libnetdata/socket/listen-sockets.c Fixed
ktsaou added 4 commits April 28, 2026 12:08
Sonar c:S1763: the `default:` case of the WndProc switch ended with
`return DefWindowProc(...);` followed by `break;`. The break was
unreachable. Remove the dead line.
…ine_sanitized

Sonar c:S1751: comm_from_cmdline_sanitized() used `while (start)` to
process the first occurrence of `comm` in the duplicated command-line
buffer, but the body unconditionally returns and `start` is never
updated -- the loop could never iterate twice. Replace with `if (start)`
for accurate intent. No behavior change.
Sonar c:S876: ebpf_update_global_publish() computed
`zombie = exit + (-release_task)` via in-place negation of the unsigned
release_task counter. The unary minus on uint64_t is well-defined
(modular arithmetic) but is a code smell and was unnecessary -- the
intent was simple subtraction.

Replace with a direct `(long)exit - (long)release_task`, matching the
pattern used for `pvc->running` two lines above. Drop the in-place
mutation; the release_task counter is not read elsewhere after this
block. Numerical result is identical.
Sonar c:S876: chart_by_reason() iterated with `size_t i` and passed
`-i` to stream_handshake_error_to_string(STREAM_HANDSHAKE). The unary
minus on the unsigned counter wrapped to a huge unsigned value that
then narrowed to int through implementation-defined conversion (C99
6.3.1.3). It happens to produce the correct -i on two's-complement
platforms but is fragile and unportable.

Change the loop variable to `int`. The loop bound
STREAM_HANDSHAKE_NEGATIVE_MAX is 40, well within int range, and the
b->rd[] array has more than enough entries for the same index. The
negation is now well-defined signed arithmetic.
ktsaou added 2 commits April 28, 2026 12:28
Sonar c:S3923: mcp_query_interrupt_callback() had a null guard
(`if (!int_data || !int_data->mcpc) return false;`) followed by an
unconditional `return false`. Both branches returned the same value
and no field of int_data was ever dereferenced, so the guard was dead
code.

Remove the conditional, mark `data` unused with `(void)data;`, and
keep the callback as an explicit "no interrupt" stub. The trailing
comment about future client-disconnect/timeout detection is preserved.
Sonar go:S3923: sortRethinkRows() switched on sortColumn but the
"durationMs" case and the default branch performed the same descending
sort by DurationMs. RethinkDB only exposes durationMs as a server-side
sort option (the only column with sortOpt: true in
rethinkRunningColumns), so the switch was always going to take one of
two identical paths.

Drop the switch and sort directly. The unused parameter is renamed to
`_` (Go idiom). Behaviour and call shape are unchanged.
mqtt_wss_client: SSL_set1_host() requires OpenSSL >= 1.1.0, but the
amazonlinux:2 and centos7 build matrices ship OpenSSL 1.0.2 and the
build broke with `undefined reference to 'SSL_set1_host'`. Switch to
X509_VERIFY_PARAM_set1_host(SSL_get0_param(ssl), host, 0), which is
available since OpenSSL 1.0.2 and provides the same hostname-verification
behaviour. No semantic change, just portability.

nd-mcp.py: the previous fix replaced `pass` with `raise` in the
reconnect-delay CancelledError handler, but the empty
`except CancelledError: raise` left the in-flight wait_task and
retry_task as orphans and the retry_event in whatever state it was in
when the cancellation arrived. Cancel both helper tasks and clear the
event before re-raising, so the cancellation does not leave dangling
state behind.
@ktsaou

ktsaou commented Apr 28, 2026

Copy link
Copy Markdown
Member Author

@cubic-dev-ai please review again

@cubic-dev-ai

cubic-dev-ai Bot commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai please review again

@ktsaou I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 25 files

Confidence score: 3/5

  • There is a concrete user-impact risk in src/aclk/mqtt_websockets/mqtt_wss_client.c: using X509_VERIFY_PARAM_set1_host alone may cause TLS certificate verification to fail for IP-literal connections even when the certificate has valid IP SAN entries.
  • Because this affects connection/authentication behavior for a real usage path (IP-based endpoints), the merge risk is moderate rather than minimal.
  • This looks scoped to verification-target handling rather than a broad architectural problem, so it should be straightforward to fix once addressed.
  • Pay close attention to src/aclk/mqtt_websockets/mqtt_wss_client.c - hostname-only verification setup can reject valid IP-based certificates.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/aclk/mqtt_websockets/mqtt_wss_client.c">

<violation number="1" location="src/aclk/mqtt_websockets/mqtt_wss_client.c:440">
P2: Handle IP literals when setting the verification target. X509_VERIFY_PARAM_set1_host only configures DNS hostname checks, so IP-based connections can fail certificate verification even with valid IP SANs.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant App as Application (ACLK/Claim/Plugin)
    participant IPC as NetIPC Service Layer
    participant TS as Transport Layer (UDS/SHM/Pipe)
    participant FS as Filesystem/OS
    participant Cloud as Cloud/MQTT Endpoint

    Note over App,Cloud: Audit-Driven Security & Robustness Hardening

    rect rgb(240, 248, 255)
    Note over App,Cloud: 1. Secure Initialization & Permissions
    App->>FS: Create lock/API/DB files
    FS-->>App: CHANGED: Enforced 0600 mode (no world-read)
    App->>FS: Create UNIX socket
    FS-->>App: CHANGED: Enforced 0666 mode (dropped exec bit)
    end

    rect rgb(255, 245, 230)
    Note over App,Cloud: 2. ACLK TLS Handshake Hardening
    App->>Cloud: mqtt_wss_connect()
    App->>App: NEW: X509_VERIFY_PARAM_set1_host()
    App->>Cloud: SSL_connect()
    alt Verification Failure
        Cloud-->>App: Cert Mismatch / Self-Signed
        opt MQTT_WSS_SSL_ALLOW_SELF_SIGNED is set
            App->>App: CHANGED: Allow Hostname/IP mismatch errors
        end
    end
    end

    rect rgb(230, 255, 230)
    Note over IPC,TS: 3. NetIPC Transport Recovery (Stale Path Checks)
    IPC->>TS: Initialize Server
    TS->>FS: lstat() / open(O_NOFOLLOW)
    alt Path exists but is stale
        TS->>TS: NEW: Verify inode/type matches expected
        TS->>FS: unlink() stale path
    else Path is live/socket active
        TS-->>IPC: Return Error (Refuse overwrite)
    end
    end

    rect rgb(245, 245, 245)
    Note over App,TS: 4. Data Transfer & Buffer Safety
    App->>IPC: Send Payload
    IPC->>IPC: NEW: Guard against size_t overflow (32-bit safety)
    IPC->>TS: Transport Send
    TS->>TS: NEW: Validate total_msg fits packet/capacity
    TS-->>IPC: Response Received
    IPC->>IPC: NEW: Check response_len against negotiated capacity
    IPC-->>App: Return Result
    end

    opt Failure Path
        App->>App: NEW: Statsd-stress socket/memory cleanup on send failure
        App->>App: NEW: nd-mcp.py cancels and drains in-flight tasks
    end
Loading

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread src/aclk/mqtt_websockets/mqtt_wss_client.c Outdated
X509_VERIFY_PARAM_set1_host() only matches against the certificate's
dNSName SAN. When the agent connects to an IP literal (e.g. an on-prem
deployment configured with 10.0.0.5 instead of a hostname), the
verification fails even if the certificate has a valid iPAddress SAN
because the IP literal is interpreted as a DNS name.

Try X509_VERIFY_PARAM_set1_ip_asc() first -- it parses the input as an
IP and matches against the iPAddress SAN, returning 0 when the input
is not a valid IP. If that fails (the typical DNS hostname case), fall
back to X509_VERIFY_PARAM_set1_host(). Both code paths are guarded by
the existing MQTT_WSS_SSL_DONT_CHECK_CERTS opt-out, and the
MQTT_WSS_SSL_ALLOW_SELF_SIGNED override already covers
X509_V_ERR_IP_ADDRESS_MISMATCH for setups whose certs do not match.
@ktsaou

ktsaou commented Apr 28, 2026

Copy link
Copy Markdown
Member Author

@cubic-dev-ai please review again

@cubic-dev-ai

cubic-dev-ai Bot commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment @cubic-dev-ai review.

@cubic-dev-ai

cubic-dev-ai Bot commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai please review again

@ktsaou I have started the AI code review. It will take a few minutes to complete.

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
23.0% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@ktsaou
ktsaou marked this pull request as ready for review April 29, 2026 15:08
@ktsaou

ktsaou commented Apr 29, 2026

Copy link
Copy Markdown
Member Author

@stelfrag you can take over this.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants