Netdata fixes part 27#22967
Conversation
string_2way_merge() keeps a common prefix and suffix while replacing the differing middle with [x]. Its suffix scan reset the cursors to the last byte of each string, dereferenced them, and then decremented them without checking the start boundary or the already-copied prefix boundary. When the shared suffix consumed the remaining bytes of the shorter side, the next loop condition could read before the string object. This is a current issue because production database context merge paths call string_2way_merge() for runtime metadata strings. Runtime values with overlapping prefix and suffix patterns can reach the invalid suffix scan. Keep the existing algorithm, but save the prefix-boundary cursors, restart the suffix cursors at one-past-end positions, and compare s1[-1] and s2[-1] only while both cursors remain above the prefix boundary. Performance impact is negligible: the fix adds pointer-bound comparisons inside the existing suffix scan, with no new allocations and no extra pass. Functional impact is limited to replacing undefined out-of-bounds reads with the documented bounded prefix/suffix merge behavior; outputs for defined non-overlapping cases are preserved. The required review gate passed with all reviewers voting production grade and absolutely necessary. (cherry picked from commit 0ab07df) (cherry picked from commit 7ad79241aa2cc6e25cad9a8e709c1ca0c4f48311)
STRING stores length including the terminating NUL in uint32_t, while string_strdupz() derives the value from a size_t strlen() result and string_strndupz() accepts a size_t len. Current callers pass bounded measured lengths, so this is a potential bug, but an oversized future or input-derived length can exceed the internal width, wrap len + 1 on 32-bit, overflow long allocation/accounting, or truncate string->length after insertion. Define the maximum data-byte length supported by both the uint32_t length field and long allocation accounting, then reject larger inputs before adding the terminating NUL byte. Valid inputs keep the same lookup, insertion, allocation, copy, refcount, and return behavior. Functional impact is limited to failing impossible unrepresentable lengths before corruption. Performance impact is negligible: two unlikely comparisons in constructor paths and no added allocation, lock, copy, or caller work. (cherry picked from commit 3f24fc9) (cherry picked from commit b062d79f56e2559812c15dafc3bf3770dd48f1d5)
Function lookup copies the sanitized command into a local MAX_FUNCTION_LENGTH buffer, but the suffix-trimming cursor used the caller-provided sanitized command length from a larger PLUGINSD_LINE_MAX buffer. Oversized function commands could therefore initialize the cursor past the local stack buffer and the trimming loop would read and write outside that buffer. Recompute the lookup length from the local copied buffer before suffix trimming. Commands that fit in the local buffer keep the same cursor and lookup behavior, while oversized commands are trimmed only within the buffer that dictionary lookup already uses. (cherry picked from commit dc05e31) (cherry picked from commit 955674c70b56f26369b5cf1be5759dc4c39ac4e7)
url_find_protocol() scanned to the next space or string terminator while looking for the HTTP protocol marker. When the scan reached the terminator, the old condition did not break and the else branch advanced the cursor past the string, so the next loop test read beyond the request buffer. Break when the scan reaches the terminator as well as when it finds the protocol marker. Valid request lines still return the same marker pointer, while partial or malformed request lines now return the existing terminator and use the caller's established incomplete-request path. (cherry picked from commit ce52849) (cherry picked from commit 79c5e36ea2efa0c10cb7f62f514af3cef69d5504)
holtwinters() read series[0] before checking whether the input series had any entries. With entries equal to zero, it could also compute estimated_level[entries - 1], wrapping the index to SIZE_MAX before checking the Holt-Winters return code. Return NAN for empty input before any array access, matching the established empty-series behavior of the neighboring statistical helpers. The non-empty path is unchanged, and no currently exercised Netdata path changes because the helper has no in-tree callers. (cherry picked from commit 233117c) (cherry picked from commit a59d89f9d64889f8322060773cee9babbf9cee3b)
duration_snprintf() accepted an arbitrary unit string and passed it through duration_find_unit(), which returns NULL for unknown non-empty units. The formatter dereferenced the lookup result unconditionally, so an invalid unit crashed before the helper could report an error. Return -3 when the duration unit lookup fails, matching the existing size_snprintf() and entries_snprintf() formatter contract. Valid units, NULL units, and empty units keep the same formatting path and output. (cherry picked from commit 3bbc19d) (cherry picked from commit 528872c9d79c45913f8cc7c3ebe6fb60435aeaaa)
c_rhash stores each value with an explicit value_type and allocates exactly the size for that type. The pointer getters matched only the key type before reading bin->value as void **, so a matching key that stored a one-byte ITEMTYPE_UINT8 value could be read as a pointer-width object and over-read the heap allocation. Require ITEMTYPE_OPAQUE_PTR before pointer getters dereference stored values as pointers. Valid pointer insert/get paths keep the same behavior, while mismatched typed pointer gets now fail safely instead of reading beyond the stored value allocation. (cherry picked from commit 976757c) (cherry picked from commit 68914ef219ebceae10a5106a6a16e674cb428b83)
rrd_functions_find_by_name() copied the requested function name into a fixed local buffer and then used strlen() to recompute the truncated key length. The buffer is expected to be terminated by strncpyz(), but the scan did not carry the local buffer bound and triggered the PR static-analysis finding. Use strnlen(buffer, sizeof(buffer)) so the post-copy key scan is explicitly bounded by the local buffer. Properly terminated buffers keep the same key length and lookup behavior. (cherry picked from commit 421c964) (cherry picked from commit 3bf0a61ea43ece8c88c9c754699d6005eaeae04d)
cbuffer_remove_unsafe() trusted callers to never remove more bytes than the ring currently holds. Current reviewed callers pass bounded socket, parser, and internal lengths, so the bug is potential in current source paths, but an invalid future or edge caller could move read past write and corrupt the ring. Clamp the requested removal to cbuffer_used_size_unsafe() before advancing read. Valid callers keep identical pointer movement and return contracts; over-removal now empties the readable portion instead of creating an invalid read cursor. Functional impact: no change for valid callers; invalid over-removal fails safe by discarding only bytes actually present. Performance impact: one inlined used-size calculation and branch per removal, negligible beside existing I/O call sites. Security impact: prevents a circular-buffer cursor corruption path from turning into later out-of-bounds logical reads. Review verdicts: required external review process passed on attempt 2; required reviewer outputs approved the classifications and patch with no blockers. Side observations were recorded in local worker artifacts, not included in this commit. (cherry picked from commit 055ba44) (cherry picked from commit 6ff1b21d365334acf6954ffa95d814087c380e91)
onewayalloc performs arena sizing with size_t additions, multiplications, and alignment rounding before handing the result to mmap or the bump allocator. The current code has unchecked arithmetic in onewayalloc_callocz(), onewayalloc_doublesize(), natural alignment of requested sizes, and page-size rounding. Overflow can wrap a huge request to a smaller allocation and then let callers or memcpy() use the original logical size. This is a potential bug by current reachability rules: the vulnerable helper code exists today, but no existing production caller has been proven to pass overflowing valid inputs. The deterministic failure mode is still allocator-internal memory corruption if such a size reaches the helper. The fix adds file-local checked add, multiply, natural-alignment, and page-alignment helpers, then routes only the vulnerable size calculations through them. Valid sizes produce the same values as before; impossible overflowing sizes now call fatal(), matching the existing z-allocation failure contract. Functional impact: no intended behavior change for valid allocation sizes. The only changed behavior is fail-fast handling for size_t arithmetic overflow that previously produced undersized arenas. Performance impact: negligible. The hot path adds simple unlikely bounds checks; multiplication guards add one size_t division only on calloc/doublesize paths, and page alignment is done when allocating pages. (cherry picked from commit 241354c) (cherry picked from commit 3ab87b9edcc7ddd81fc1f99e2be203bc1accec67)
Issue: object_state_deactivate() waits for state_refcount to equal OBJECT_STATE_DEACTIVATED after shifting active holders into the sentinel range. object_state_release() used an unconditional atomic decrement, so an imbalanced release at the deactivated sentinel could push the counter below the only terminal value and leave the deactivation wait loop spinning forever. Current vs potential: this is a current helper invariant bug with a potential trigger. Balanced callers are safe, but an acquire/release imbalance or memory corruption can corrupt the sentinel termination state. Solution: make release use a CAS loop with the same release ordering as the old atomic subtract and reject releases at or below OBJECT_STATE_DEACTIVATED. Make the deactivation wait loop fatal if it observes a value below the sentinel. Functional impact: balanced acquire/release, activation, and deactivation transitions are unchanged. Invalid releases at or below the deactivated sentinel now fail fast instead of silently corrupting the deactivation terminal state. Performance impact: the release path changes from one atomic subtract to one successful CAS in the normal case. Deactivation adds one comparison per wait iteration. Security and reliability impact: prevents an unbounded deactivation wait and signed sentinel underflow caused by invalid release imbalance at the deactivated sentinel. Validation: git submodule update --init --recursive; git diff --check -- src/libnetdata/object-state/object-state.c; cmake configure with debugfs enabled and only xenstat disabled for the missing local pkg-config dependency; focused object-state object build; libnetdata build in the worker worktree. Reviewer gate: required three-reviewer gate completed successfully on attempt 1 in the worker slot. All required semantic reviews approved the final diff with no blockers. The adjacent active-zero extra-release state-machine issue was recorded separately as decision-required and is not changed by this commit. (cherry picked from commit baeed22) (cherry picked from commit 1d10ef8069f7d3498e0041aedf9ed9607e06ca50)
Current issue: quoted_strings_splitter() and tc_split_words() stored the first parsed word before checking caller-provided word-array capacity. Current in-tree callers pass positive capacities, so this is a latent bug; a future zero-capacity caller could write outside its words array and get an incorrect non-zero word count. Solution: return immediately for zero or non-positive capacity before parsing, mutating the input string, or writing through words. Functional impact: existing positive-capacity callers keep the same behavior. The audited call sites pass positive constants or _countof() of fixed arrays. Zero-capacity calls now return no words and perform no writes. Performance impact: the change adds one entry guard on each splitter. Existing call sites use positive constants, so optimized builds can fold it away or predict it as not taken. No allocation, I/O, locking, or extra loop is introduced. Security impact: removes a latent out-of-bounds write path without adding any input surface, privilege change, or parser expansion. Validation: git diff --check passed. CMake configured with optional features disabled. The touched line_splitter and tc plugin objects built successfully. A zero-capacity probe verified no write and no input mutation for quoted_strings_splitter(). Independent source review: required independent reviewers approved the source diff with no source-backed blockers. Non-blocking notes are recorded in the worker summary. (cherry picked from commit 9281742) (cherry picked from commit cc6256200c7e96840de5a540c945007227e821b8)
MEM-linked_lists-005 is a potential list-cycle bug: DOUBLE_LINKED_LIST_APPEND_LIST_UNSAFE() could splice a list into itself when head2 == head, setting the old tail next pointer back to the head and making forward traversal loop forever. The current debugfs powercap caller builds fresh disjoint zone lists, so no current runtime trigger was found and valid caller behavior is unchanged. The fix caches head2 once and treats exact self-append as a no-op before the normal constant-time splice. Normal append-list cost adds one pointer assignment and one pointer comparison; the invalid self-append path avoids unbounded traversal. Functional impact is limited to invalid exact self-append inputs; valid list appends keep the same behavior and layout. Performance impact is one extra pointer compare on normal append-list calls. (cherry picked from commit e69e4f8) (cherry picked from commit 92afee93d638f8e0262e5c704a2fb12b54fac4cd)
MEM-url-003 is a potential integer overflow in URL-style escaped-string allocation. url_encode() sized its worst-case output as strlen(str) * 3 + 1, and dyncfg_escape_id_for_filename() used the same pattern for DynCfg file/schema identifiers. On 32-bit size_t, an extremely large input can wrap before mallocz(), producing an undersized allocation that the following percent-escape loop can overrun. Current vs potential: the code paths are current. url_encode() is used by buffer_key_value_urlencode() for streaming and host/system metadata, and the DynCfg escaper is used for file/schema paths. The trigger is potential because it requires an input length above (SIZE_MAX - 1) / 3, mainly relevant to 32-bit builds. Functional impact: normal encoded output, allocation ownership, and caller contracts are unchanged for inputs below the overflow boundary. Inputs that cannot be represented safely now fail through the existing fatal allocation-style path before any wrapped allocation can be used. Performance impact: the fix adds one unlikely bounds branch per call and reuses the measured length for allocation, so it does not add an extra strlen() or steady-state allocation cost. Solution: cache the input length, reject lengths that would overflow len * 3 + 1, and allocate only after the guard. (cherry picked from commit 6026445) (cherry picked from commit 79290f1e7fa56da3068000fe78c60135063c5f48)
refcount_release() and refcount_release_and_acquire_for_deletion_advanced() treated 0 and REFCOUNT_DELETED as valid release inputs because both pass REFCOUNT_VALID(). That allowed a double release from 0 to store -1, or a release after completed deletion to store below REFCOUNT_DELETED, masking the caller that violated ownership until a later operation noticed the corrupted counter. This is a potential bug: the current source call sites pair release helpers with successful acquire paths, and no current reviewed path legitimately releases from either terminal state. Reject those terminal states before each helper's CAS. Normal positive releases, including 1->0, deletion-acquire 1->REFCOUNT_DELETED, positive N>1->N-1, and deletion-wait releases such as REFCOUNT_DELETED+1->REFCOUNT_DELETED keep the same behavior and memory ordering. Functional impact: invalid terminal-state releases now fatal at the offending caller instead of corrupting the refcount first. Valid release behavior is unchanged. Performance impact: one unlikely branch with two integer comparisons in each existing CAS loop; no additional atomic operations or fences. Bug status: potential robustness bug fixed in the generic refcount release helpers. (cherry picked from commit ad52c84) (cherry picked from commit 9b77e4e6fb82f9dd11699fa3520bda560063b45e)
(cherry picked from commit 2c84001) (cherry picked from commit 27d4778e11cb962237575d3aeb43cf8f7f4e2d14)
(cherry picked from commit 90b41b9) (cherry picked from commit 9d3386b7264bdbbe80f11fb09592b320ad8ad879)
rbuf_find_bytes() scans up to the ringbuffer byte-count domain, but reported the found offset through an int pointer. On a ringbuffer whose readable data exceeds INT_MAX bytes, a full scan or late match could overflow the signed output counter even though the buffer itself is sized and indexed with size_t. Current vs potential bug: current ACLK HTTP and WebSocket users keep buffers below INT_MAX, so this is not a known currently exercised production corruption path in those callers. The generic ringbuffer API nevertheless had a deterministic type-width mismatch for large buffers. Solution: change rbuf_find_bytes() to report offsets through size_t *, update every direct in-repo caller to use size_t local indexes, and adjust WebSocket diagnostics to use size_t formatting. Focused unit coverage now verifies match and no-match offsets. Functional impact: existing parsing behavior and return codes are unchanged. Offsets still represent bytes from the current tail; the output type now matches the rest of the ringbuffer capacity and byte-count API. Performance impact: neutral. The scan loop still increments one pointer and one offset per byte; the offset type now uses the native size_t width already used by the surrounding ringbuffer code. Validation: inspected the staged diff and all direct rbuf_find_bytes() call sites. Worker validation passed whitespace checks, changed object compilation, a local ringbuffer unittest runner, and reviewer quorum after rerunning an incomplete reviewer output; a broader netdata target stopped on an unrelated missing ACLK proto after changed objects compiled. (cherry picked from commit 17b2c7f) (cherry picked from commit 066d605ba2afd93e70b290948e4c98921181132f)
string_2way_merge() lazily initialized the file-local [x] sentinel on first use by reading and writing string_2way_merge_X without synchronization. Two concurrent first callers could race on that global pointer and acquire extra interned-string references while cleanup releases only the single owned sentinel reference. This is a potential bug in current runtime paths: context merge callers can exercise string_2way_merge() after startup, but no deterministic reproduction was produced. The source race and possible leaked reference are clear from the unsynchronized lazy initialization. Initialize the sentinel once from string_init(), after all string partition locks are initialized, and remove the runtime lazy assignment from string_2way_merge(). string_destroy() continues to release the single owned sentinel reference and clear the pointer during sanitizer cleanup. Functional impact: none for valid initialized use. string_2way_merge() keeps the same signature, returned ownership, NULL handling, sentinel content, and merge output for all current callers. Performance impact: neutral to slightly positive. Runtime merge calls lose one unlikely branch and first-call allocation path; startup gains one small interned string allocation. Validation: git diff --cached --check passed after cherry-pick onto the current branch. Worker validation built libnetdata with low priority and ran a temporary string_init/string_unittest/string_destroy harness with 0 errors; full netdata build was blocked by unrelated missing ACLK proto checkout content. Local source-review gate verified startup, caller, and shutdown ordering. (cherry picked from commit b52a243) (cherry picked from commit 611056a3185c4364eaf1c223d5d75ac68a06a11d)
ks_2samp() compared baseline indexes with highlight indexes scaled by base_shifts using int delta = base_idx - (high_idx << base_shifts). If base_shifts was too large, or the scaled highlight index exceeded the signed range, the left shift and subtraction could invoke undefined behavior before the later probability checks. This is a latent implementation bug in a helper used by current metric-correlation paths. Current API and MCP callers bound points and shifts so valid requests do not reach the invalid shift today, but the helper itself accepted out-of-contract internal inputs and had no local representability guard. Reject unrepresentable scaled-index inputs with NAN before sorting, build the power-of-two multiplier only after bounding base_shifts, and widen the intermediate delta/min/max comparisons to int64_t. The guard uses high_size because every high_idx returned by binary_search_bigger_than() is bounded by high_size. Functional impact: none for valid current callers. Existing API, MCP, JSON, configuration, and query contracts are unchanged; impossible or future out-of-contract inputs now return NAN, which the existing correlation result path already treats as a non-result. Performance impact: negligible. The hot path adds one unlikely guard and a bounded multiplier loop per ks_2samp() call, then uses int64_t comparisons in loops already dominated by qsort(), binary searches, and probability calculation. Validation: git diff --cached --check passed. Isolated validation configured a minimal low-priority build, linked netdata, and passed the existing -W mctest path including the new invalid-shift NAN case; broader build setup was blocked by unrelated local dependency/submodule gaps before the minimal build was prepared. Reviewer gate verified caller bounds, shift representability, NAN handling, and no caller-visible behavior change for valid requests. (cherry picked from commit 247869b) (cherry picked from commit 95d49b90deed47c6e1d62b3bf9bc5b523b6157da)
There was a problem hiding this comment.
No issues found across 23 files
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
Architecture diagram
sequenceDiagram
participant OWA as OnewayAlloc
participant Dict as Dictionary
participant CBuf as Circular Buffer
participant RBuf as Ring Buffer
participant OS as Object State
participant RF as Refcount
participant Eval as Eval Engine
participant Str as STRING
participant Enc as URL/DynCfg
participant Stats as Statistics
Note over OWA: Overflow-safe arithmetic
OWA->>OWA: onewayalloc_mallocz(size)
OWA->>OWA: onewayalloc_natural_alignment_or_fatal(size)
OWA->>OWA: onewayalloc_add_or_fatal (size + align)
alt overflow
OWA->>OWA: fatal()
end
OWA->>OWA: onewayalloc_callocz(nmemb, size)
OWA->>OWA: onewayalloc_mul_or_fatal (nmemb * size)
alt overflow
OWA->>OWA: fatal()
end
OWA->>OWA: onewayalloc_doublesize(src, oldsize)
OWA->>OWA: onewayalloc_mul_or_fatal (oldsize * 2)
alt overflow
OWA->>OWA: fatal()
end
OWA->>OWA: page-aligned allocation
OWA->>OWA: onewayalloc_page_alignment_or_fatal(size, page_size)
alt overflow in alignment padding
OWA->>OWA: fatal()
end
Note over Dict: Key/value length bounds enforced
Dict->>Dict: dictionary_set_advanced(name, name_len, value, value_len)
Dict->>Dict: api_is_name_good() checks real length <= KEY_LEN_MAX
alt length > KEY_LEN_MAX
Dict->>Dict: return NULL (reject)
else length ok
Dict->>Dict: check value_len <= VALUE_LEN_MAX
alt value_len > VALUE_LEN_MAX
Dict->>Dict: return NULL (reject)
end
end
Dict->>Dict: dictionary_view_set_advanced() same checks
Note over CBuf: Safe removal clamping
CBuf->>CBuf: cbuffer_remove_unsafe(num)
CBuf->>CBuf: cbuffer_used_size_unsafe()
alt num > used
CBuf->>CBuf: clamp num = used
end
CBuf->>CBuf: buf->read += num (bounded)
Note over RBuf: find_bytes returns size_t offset
RBuf->>RBuf: rbuf_find_bytes(needle, needle_bytes, &found_idx)
RBuf-->>RBuf: found_idx now size_t*
RBuf-->>Caller: pointer or NULL
Note over RBuf: Unit test validates offset==4 on success, idx==8 on no match
Note over OS,RF: Concurrency hardening
OS->>OS: object_state_deactivate()
OS->>OS: CAS os->state_refcount -> OBJECT_STATE_DEACTIVATED
loop wait for holders
OS->>OS: load state_refcount
alt refcount < OBJECT_STATE_DEACTIVATED
OS->>OS: fatal (underflow)
else refcount == DEACTIVATED
OS->>OS: break
end
end
OS->>OS: object_state_release()
loop CAS
OS->>OS: load expected refcount
alt expected <= OBJECT_STATE_DEACTIVATED
OS->>OS: fatal (no holders)
else
OS->>OS: desired = expected - 1
OS->>OS: CAS
end
end
RF->>RF: refcount_release()
loop CAS
RF->>RF: load expected
alt expected == 0 or REFCOUNT_DELETED
RF->>RF: fatal (invalid release)
else
RF->>RF: desired = expected - 1, CAS
end
end
RF->>RF: refcount_release_and_acquire_for_deletion_advanced()
alt expected == 0 or REFCOUNT_DELETED
RF->>RF: fatal
else expected == 1
RF->>RF: set REFCOUNT_DELETED
end
Note over Eval: Atomic id generation
Eval->>Eval: eval_node_alloc(count)
Eval->>Eval: __atomic_add_fetch(&id, 1, RELAXED) for node->id
Note over Str: Length bound on interned strings
Str->>Str: string_strdupz(str)
Str->>Str: check length <= STRING_MAX_LENGTH
alt length too long
Str->>Str: fatal()
else
Str->>Str: proceed with hash and index
end
Str->>Str: string_strndupz(str, len) same check
Note over Str: [x] sentinel initialization
Str->>Str: string_2way_merge_X = string_strdupz("[x]")
Note over Str: Moved to string_init() to ensure early init
Note over Enc: Overflow guard before allocation
Enc->>Enc: dyncfg_escape_id_for_filename(id)
Enc->>Enc: compute max_len = (SIZE_MAX-1)/3
alt len > max_len
Enc->>Enc: fatal()
else
Enc->>Enc: mallocz(len * 3 + 1)
end
Enc->>Enc: url_encode(str)
Enc->>Enc: same overflow check
alt len > (SIZE_MAX-1)/3
Enc->>Enc: fatal()
else
Enc->>Enc: mallocz(len * 3 + 1)
end
Note over Stats: Input validation and overflow-safe math
Stats->>Stats: holtwinters(series, entries, ...)
alt entries == 0
Stats->>Stats: return NAN
end
Stats->>Stats: ks_2samp(baseline, base_size, highlight, high_size, base_shifts)
alt base_shifts >= sizeof(int64_t)*CHAR_BIT - 1
Stats->>Stats: return NAN
else high_size * 2^base_shifts would overflow
Stats->>Stats: return NAN
else
Stats->>Stats: compute int64_t delta = base_idx - (int64_t)high_idx * high_multiplier
end
Stats->>Stats: duration_snprintf(value, unit)
alt unit is invalid (duration_find_unit returns NULL)
Stats->>Stats: return -3
end
Note over Dict: Linked list self-append guard (in macro)
Dict->>Dict: DOUBLE_LINKED_LIST_APPEND_LIST_UNSAFE(head, head2)
alt head2 == head
Dict->>Dict: skip (no-op)
else
Dict->>Dict: perform append
end
|
There was a problem hiding this comment.
Pull request overview
This PR continues the “Netdata fixes” series by hardening memory safety and correctness in several core C subsystems (parsers, ringbuffers, atomics/refcounting, dictionary bounds, and statistical routines), primarily by adding bounds checks, overflow-safe arithmetic, and safer types in index math.
Changes:
- Added overflow/bounds guards in URL encoding/escaping, allocator arithmetic, dictionary key/value sizing, and KS2 scaling math.
- Switched several parsing/index variables to
size_tand tightened edge-case handling (e.g., empty Holt-Winters input, invalid duration units, circular buffer removal clamp). - Added/extended unit tests for dictionary length bounds and ringbuffer “find bytes” behavior.
Reviewed changes
Copilot reviewed 23 out of 23 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/web/api/queries/weights.c | Overflow-safe KS2 delta math + unit test for invalid shift handling. |
| src/libnetdata/url/url.c | Prevent URL-encode allocation overflow; fix protocol scan end-of-string handling. |
| src/libnetdata/string/string.c | Enforce max interned-string length; harden two-way merge suffix scan; init “[x]” sentinel during string_init(). |
| src/libnetdata/statistical/statistical.c | Return NAN for Holt-Winters when called with empty input. |
| src/libnetdata/ringbuffer/ringbuffer.h | Update rbuf_find_bytes() API to report offsets via size_t *. |
| src/libnetdata/ringbuffer/ringbuffer.c | Align rbuf_find_bytes() implementation with size_t offset reporting. |
| src/libnetdata/ringbuffer/ringbuffer-unittest.c | Add unit tests for rbuf_find_bytes() pointer/offset behavior. |
| src/libnetdata/parsers/duration.c | Return an error for invalid duration units instead of dereferencing NULL. |
| src/libnetdata/onewayalloc/onewayalloc.c | Add overflow-safe add/mul/alignment helpers for allocation sizing. |
| src/libnetdata/object-state/object-state.c | Add underflow detection and CAS-based release guardrails for state refcounting. |
| src/libnetdata/linked_lists/linked_lists.h | Prevent double-linked list self-append and evaluate head2 once. |
| src/libnetdata/line_splitter/line_splitter.h | Early return on zero-capacity splitter to avoid invalid writes. |
| src/libnetdata/inicfg/dyncfg.c | Prevent dyncfg filename-escape allocation overflow. |
| src/libnetdata/eval/eval-utils.c | Make eval node IDs atomic to avoid concurrent ID races. |
| src/libnetdata/dictionary/dictionary.c | Enforce key/value length bounds and avoid unbounded scans in validation paths. |
| src/libnetdata/dictionary/dictionary-unittest.c | Add tests ensuring oversized key/value lengths are rejected safely. |
| src/libnetdata/circular_buffer/circular_buffer.c | Clamp removals to used bytes to prevent read pointer jumping past write pointer. |
| src/libnetdata/c_rhash/c_rhash.c | Require pointer value type for pointer getters to prevent misuse. |
| src/libnetdata/atomics/refcount.h | Reject invalid releases from 0/DELETED refcounts; strengthen deletion-path checks. |
| src/database/rrdfunctions.c | Bound function-name scanning via strnlen() after copying into a fixed buffer. |
| src/collectors/tc.plugin/plugin_tc.c | Guard word-splitting helper against non-positive max_words. |
| src/aclk/mqtt_websockets/ws_client.c | Switch header parsing indexes/log formatting to size_t for safer bounds handling. |
| src/aclk/https_client.c | Switch HTTP parsing indexes to size_t for safer bounds handling. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
thiagoftsm
left a comment
There was a problem hiding this comment.
PR is running as expected. LGTM!



Summary
Summary by cubic
Hardened memory safety and correctness across core paths with bounds checks, safer arithmetic, and type fixes; added unit tests to cover edge cases. No behavior changes for valid inputs; prevents rare crashes, hangs, and overflows.
rbuf_find_bytes()now returns offsets via size_t*.onewayalloc; enforced dictionary key/value length bounds; validated interned string length; fixed two-way merge suffix bounds and initialized the “[x]” sentinel at startup.holtwinters()returns NAN on empty input;duration_snprintf()returns -3 on invalid unit; guarded KS2 index scaling and widened deltas to int64_t.c_rhashpointer getters now require pointer value type; bounded function-name scan with strnlen.Written for commit d27a3aa. Summary will update on new commits.